mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 18:11:27 -07:00
update skills
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
# Cloudflare Workers Smart Placement
|
||||
|
||||
Automatic workload placement optimization to minimize latency by running Workers closer to backend infrastructure rather than end users.
|
||||
|
||||
## Core Concept
|
||||
|
||||
Smart Placement automatically analyzes Worker request duration across Cloudflare's global network and intelligently routes requests to optimal data center locations. Instead of defaulting to the location closest to the end user, Smart Placement can forward requests to locations closer to backend infrastructure when this reduces overall request duration.
|
||||
|
||||
### When to Use
|
||||
|
||||
**Enable Smart Placement when:**
|
||||
- Worker makes multiple round trips to backend services/databases
|
||||
- Backend infrastructure is geographically concentrated
|
||||
- Request duration dominated by backend latency rather than network latency from user
|
||||
- Running backend logic in Workers (APIs, data aggregation, SSR with DB calls)
|
||||
- Worker uses `fetch` handler (not RPC methods)
|
||||
|
||||
**Do NOT enable for:**
|
||||
- Workers serving only static content or cached responses
|
||||
- Workers without significant backend communication
|
||||
- Pure edge logic (auth checks, redirects, simple transformations)
|
||||
- Workers without fetch event handlers
|
||||
- Workers with RPC methods or named entrypoints (only `fetch` handlers are affected)
|
||||
- Pages/Assets Workers with `run_worker_first = true` (degrades asset serving)
|
||||
|
||||
### Decision Tree
|
||||
|
||||
```
|
||||
Does your Worker have a fetch handler?
|
||||
├─ No → Smart Placement won't work (skip)
|
||||
└─ Yes
|
||||
│
|
||||
Does it make multiple backend calls (DB/API)?
|
||||
├─ No → Don't enable (won't help)
|
||||
└─ Yes
|
||||
│
|
||||
Is backend geographically concentrated?
|
||||
├─ No (globally distributed) → Probably won't help
|
||||
└─ Yes or uncertain
|
||||
│
|
||||
Does it serve static assets with run_worker_first=true?
|
||||
├─ Yes → Don't enable (will hurt performance)
|
||||
└─ No → Enable Smart Placement
|
||||
│
|
||||
After 15min, check placement_status
|
||||
├─ SUCCESS → Monitor metrics
|
||||
├─ INSUFFICIENT_INVOCATIONS → Need more traffic
|
||||
└─ UNSUPPORTED_APPLICATION → Disable (hurting performance)
|
||||
```
|
||||
|
||||
### Key Architecture Pattern
|
||||
|
||||
**Recommended:** Split full-stack applications into separate Workers:
|
||||
```
|
||||
User → Frontend Worker (at edge, close to user)
|
||||
↓ Service Binding
|
||||
Backend Worker (Smart Placement enabled, close to DB/API)
|
||||
↓
|
||||
Database/Backend Service
|
||||
```
|
||||
|
||||
This maintains fast, reactive frontends while optimizing backend latency.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```jsonc
|
||||
// wrangler.jsonc
|
||||
{
|
||||
"placement": {
|
||||
"mode": "smart" // or "off" to explicitly disable
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Deploy and wait 15 minutes for analysis. Check status via API or dashboard metrics.
|
||||
|
||||
**To disable:** Set `"mode": "off"` or remove `placement` field entirely (both equivalent).
|
||||
|
||||
## Requirements
|
||||
|
||||
- Wrangler 2.20.0+
|
||||
- Analysis time: Up to 15 minutes after enabling
|
||||
- Traffic requirements: Consistent traffic from multiple global locations
|
||||
- Available on all Workers plans (Free, Paid, Enterprise)
|
||||
|
||||
## Placement Status Values
|
||||
|
||||
```typescript
|
||||
type PlacementStatus =
|
||||
| undefined // Not yet analyzed
|
||||
| 'SUCCESS' // Successfully optimized
|
||||
| 'INSUFFICIENT_INVOCATIONS' // Not enough traffic
|
||||
| 'UNSUPPORTED_APPLICATION'; // Made Worker slower (reverted)
|
||||
```
|
||||
|
||||
## CLI Commands
|
||||
|
||||
```bash
|
||||
# Deploy with Smart Placement
|
||||
wrangler deploy
|
||||
|
||||
# Check placement status
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/services/$WORKER_NAME \
|
||||
| jq .result.placement_status
|
||||
|
||||
# Monitor
|
||||
wrangler tail your-worker-name --header cf-placement
|
||||
```
|
||||
|
||||
## Reading Order
|
||||
|
||||
**First time?** Start here:
|
||||
1. This README - understand core concepts and when to use Smart Placement
|
||||
2. [configuration.md](./configuration.md) - set up wrangler.jsonc and understand limitations
|
||||
3. [patterns.md](./patterns.md) - see practical examples for your use case
|
||||
4. [api.md](./api.md) - monitor and verify Smart Placement is working
|
||||
5. [gotchas.md](./gotchas.md) - troubleshoot common issues
|
||||
|
||||
**Quick lookup:**
|
||||
- "Should I enable Smart Placement?" → See "When to Use" above
|
||||
- "How do I configure it?" → [configuration.md](./configuration.md)
|
||||
- "How do I split frontend/backend?" → [patterns.md](./patterns.md)
|
||||
- "Why isn't it working?" → [gotchas.md](./gotchas.md)
|
||||
|
||||
## In This Reference
|
||||
|
||||
- [configuration.md](./configuration.md) - wrangler.jsonc setup, mode values, validation rules
|
||||
- [api.md](./api.md) - Placement Status API, cf-placement header, monitoring
|
||||
- [patterns.md](./patterns.md) - Frontend/backend split, database workers, SSR patterns
|
||||
- [gotchas.md](./gotchas.md) - Troubleshooting INSUFFICIENT_INVOCATIONS, performance issues
|
||||
|
||||
## See Also
|
||||
|
||||
- [workers](../workers/) - Worker runtime and fetch handlers
|
||||
- [d1](../d1/) - D1 database that benefits from Smart Placement
|
||||
- [durable-objects](../durable-objects/) - Durable Objects with backend logic
|
||||
- [bindings](../bindings/) - Service bindings for frontend/backend split
|
||||
@@ -0,0 +1,183 @@
|
||||
# Smart Placement API
|
||||
|
||||
## Placement Status API
|
||||
|
||||
Query Worker placement status via Cloudflare API:
|
||||
|
||||
```bash
|
||||
curl -X GET "https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/workers/services/{WORKER_NAME}" \
|
||||
-H "Authorization: Bearer <TOKEN>" \
|
||||
-H "Content-Type: application/json"
|
||||
```
|
||||
|
||||
Response includes `placement_status` field:
|
||||
|
||||
```typescript
|
||||
type PlacementStatus =
|
||||
| undefined // Not yet analyzed
|
||||
| 'SUCCESS' // Successfully optimized
|
||||
| 'INSUFFICIENT_INVOCATIONS' // Not enough traffic
|
||||
| 'UNSUPPORTED_APPLICATION'; // Made Worker slower (reverted)
|
||||
```
|
||||
|
||||
## Status Meanings
|
||||
|
||||
**`undefined` (not present)**
|
||||
- Worker not yet analyzed
|
||||
- Always runs at default edge location closest to user
|
||||
|
||||
**`SUCCESS`**
|
||||
- Analysis complete, Smart Placement active
|
||||
- Worker runs in optimal location (may be edge or remote)
|
||||
|
||||
**`INSUFFICIENT_INVOCATIONS`**
|
||||
- Not enough requests to make placement decision
|
||||
- Requires consistent multi-region traffic
|
||||
- Always runs at default edge location
|
||||
|
||||
**`UNSUPPORTED_APPLICATION`** (rare, <1% of Workers)
|
||||
- Smart Placement made Worker slower
|
||||
- Placement decision reverted
|
||||
- Always runs at edge location
|
||||
- Won't be re-analyzed until redeployed
|
||||
|
||||
## cf-placement Header (Beta)
|
||||
|
||||
Smart Placement adds response header indicating routing decision:
|
||||
|
||||
```typescript
|
||||
// Remote placement (Smart Placement routed request)
|
||||
"cf-placement: remote-LHR" // Routed to London
|
||||
|
||||
// Local placement (default edge routing)
|
||||
"cf-placement: local-EWR" // Stayed at Newark edge
|
||||
```
|
||||
|
||||
Format: `{placement-type}-{IATA-code}`
|
||||
- `remote-*` = Smart Placement routed to remote location
|
||||
- `local-*` = Stayed at default edge location
|
||||
- IATA code = nearest airport to data center
|
||||
|
||||
**Warning:** Beta feature, may be removed before GA.
|
||||
|
||||
## Detecting Smart Placement in Code
|
||||
|
||||
**Note:** `cf-placement` header is a beta feature and may change or be removed.
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const placementHeader = request.headers.get('cf-placement');
|
||||
|
||||
if (placementHeader?.startsWith('remote-')) {
|
||||
const location = placementHeader.split('-')[1];
|
||||
console.log(`Smart Placement routed to ${location}`);
|
||||
} else if (placementHeader?.startsWith('local-')) {
|
||||
const location = placementHeader.split('-')[1];
|
||||
console.log(`Running at edge location ${location}`);
|
||||
}
|
||||
|
||||
return new Response('OK');
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
## Request Duration Metrics
|
||||
|
||||
Available in Cloudflare dashboard when Smart Placement enabled:
|
||||
|
||||
**Workers & Pages → [Your Worker] → Metrics → Request Duration**
|
||||
|
||||
Shows histogram comparing:
|
||||
- Request duration WITH Smart Placement (99% of traffic)
|
||||
- Request duration WITHOUT Smart Placement (1% baseline)
|
||||
|
||||
**Request Duration vs Execution Duration:**
|
||||
- **Request duration:** Total time from request arrival to response delivery (includes network latency)
|
||||
- **Execution duration:** Time Worker code actively executing (excludes network waits)
|
||||
|
||||
Use request duration to measure Smart Placement impact.
|
||||
|
||||
### Interpreting Metrics
|
||||
|
||||
| Metric Comparison | Interpretation | Action |
|
||||
|-------------------|----------------|--------|
|
||||
| WITH < WITHOUT | Smart Placement helping | Keep enabled |
|
||||
| WITH ≈ WITHOUT | Neutral impact | Consider disabling to free resources |
|
||||
| WITH > WITHOUT | Smart Placement hurting | Disable with `mode: "off"` |
|
||||
|
||||
**Why Smart Placement might hurt performance:**
|
||||
- Worker primarily serves static assets or cached content
|
||||
- Backend services are globally distributed (no single optimal location)
|
||||
- Worker has minimal backend communication
|
||||
- Using Pages with `assets.run_worker_first = true`
|
||||
|
||||
**Typical improvements when Smart Placement helps:**
|
||||
- 20-50% reduction in request duration for database-heavy Workers
|
||||
- 30-60% reduction for Workers making multiple backend API calls
|
||||
- Larger improvements when backend is geographically concentrated
|
||||
|
||||
## Monitoring Commands
|
||||
|
||||
```bash
|
||||
# Tail Worker logs
|
||||
wrangler tail your-worker-name
|
||||
|
||||
# Tail with filters
|
||||
wrangler tail your-worker-name --status error
|
||||
wrangler tail your-worker-name --header cf-placement
|
||||
|
||||
# Check placement status via API
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/workers/services/$WORKER_NAME \
|
||||
| jq .result.placement_status
|
||||
```
|
||||
|
||||
## TypeScript Types
|
||||
|
||||
```typescript
|
||||
// Placement status returned by API (field may be absent)
|
||||
type PlacementStatus =
|
||||
| 'SUCCESS'
|
||||
| 'INSUFFICIENT_INVOCATIONS'
|
||||
| 'UNSUPPORTED_APPLICATION'
|
||||
| undefined;
|
||||
|
||||
// Placement configuration in wrangler.jsonc
|
||||
type PlacementMode = 'smart' | 'off';
|
||||
|
||||
interface PlacementConfig {
|
||||
mode: PlacementMode;
|
||||
// Legacy fields (deprecated/removed):
|
||||
// hint?: string; // REMOVED - no longer supported
|
||||
}
|
||||
|
||||
// Explicit placement (separate feature from Smart Placement)
|
||||
interface ExplicitPlacementConfig {
|
||||
region?: string;
|
||||
host?: string;
|
||||
hostname?: string;
|
||||
// Cannot combine with mode field
|
||||
}
|
||||
|
||||
// Worker metadata from API response
|
||||
interface WorkerMetadata {
|
||||
placement?: PlacementConfig | ExplicitPlacementConfig;
|
||||
placement_status?: PlacementStatus;
|
||||
}
|
||||
|
||||
// Service Binding for backend Worker
|
||||
interface Env {
|
||||
BACKEND_SERVICE: Fetcher; // Service Binding to backend Worker
|
||||
DATABASE: D1Database;
|
||||
}
|
||||
|
||||
// Example Worker with Service Binding
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
// Forward to backend Worker with Smart Placement enabled
|
||||
const response = await env.BACKEND_SERVICE.fetch(request);
|
||||
return response;
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
@@ -0,0 +1,196 @@
|
||||
# Smart Placement Configuration
|
||||
|
||||
## wrangler.jsonc Setup
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"$schema": "./node_modules/wrangler/config-schema.json",
|
||||
"placement": {
|
||||
"mode": "smart"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Placement Mode Values
|
||||
|
||||
| Mode | Behavior |
|
||||
|------|----------|
|
||||
| `"smart"` | Enable Smart Placement - automatic optimization based on traffic analysis |
|
||||
| `"off"` | Explicitly disable Smart Placement - always run at edge closest to user |
|
||||
| Not specified | Default behavior - run at edge closest to user (same as `"off"`) |
|
||||
|
||||
**Note:** Smart Placement vs Explicit Placement are separate features. Smart Placement (`mode: "smart"`) uses automatic analysis. For manual placement control, see explicit placement options (`region`, `host`, `hostname` fields - not covered in this reference).
|
||||
|
||||
## Frontend + Backend Split Configuration
|
||||
|
||||
### Frontend Worker (No Smart Placement)
|
||||
|
||||
```jsonc
|
||||
// frontend-worker/wrangler.jsonc
|
||||
{
|
||||
"name": "frontend",
|
||||
"main": "frontend-worker.ts",
|
||||
// No "placement" - runs at edge
|
||||
"services": [
|
||||
{
|
||||
"binding": "BACKEND",
|
||||
"service": "backend-api"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Backend Worker (Smart Placement Enabled)
|
||||
|
||||
```jsonc
|
||||
// backend-api/wrangler.jsonc
|
||||
{
|
||||
"name": "backend-api",
|
||||
"main": "backend-worker.ts",
|
||||
"placement": {
|
||||
"mode": "smart"
|
||||
},
|
||||
"d1_databases": [
|
||||
{
|
||||
"binding": "DATABASE",
|
||||
"database_id": "xxx"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Requirements & Limitations
|
||||
|
||||
### Requirements
|
||||
- **Wrangler version:** 2.20.0+
|
||||
- **Analysis time:** Up to 15 minutes
|
||||
- **Traffic requirements:** Consistent multi-location traffic
|
||||
- **Workers plan:** All plans (Free, Paid, Enterprise)
|
||||
|
||||
### What Smart Placement Affects
|
||||
|
||||
**CRITICAL LIMITATION - Smart Placement ONLY Affects `fetch` Handlers:**
|
||||
|
||||
Smart Placement is fundamentally limited to Workers with default `fetch` handlers. This is a key architectural constraint.
|
||||
|
||||
- ✅ **Affects:** `fetch` event handlers ONLY (the default export's fetch method)
|
||||
- ❌ **Does NOT affect:**
|
||||
- RPC methods (Service Bindings with `WorkerEntrypoint` - see example below)
|
||||
- Named entrypoints (exports other than `default`)
|
||||
- Workers without `fetch` handlers
|
||||
- Queue consumers, scheduled handlers, or other event types
|
||||
|
||||
**Example - Smart Placement ONLY affects `fetch`:**
|
||||
```typescript
|
||||
// ✅ Smart Placement affects this:
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
// This runs close to backend when Smart Placement enabled
|
||||
const data = await env.DATABASE.prepare('SELECT * FROM users').all();
|
||||
return Response.json(data);
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ Smart Placement DOES NOT affect these:
|
||||
export class MyRPC extends WorkerEntrypoint {
|
||||
async myMethod() {
|
||||
// This ALWAYS runs at edge, Smart Placement has NO EFFECT
|
||||
const data = await this.env.DATABASE.prepare('SELECT * FROM users').all();
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export async function scheduled(event: ScheduledEvent, env: Env) {
|
||||
// NOT affected by Smart Placement
|
||||
}
|
||||
```
|
||||
|
||||
**Consequence:** If your backend logic uses RPC methods (`WorkerEntrypoint`), Smart Placement cannot optimize those calls. You must use fetch-based patterns for Smart Placement to work.
|
||||
|
||||
**Solution:** Convert RPC methods to fetch endpoints, or use a wrapper Worker with `fetch` handler that calls your backend RPC (though this adds latency).
|
||||
|
||||
### Baseline Traffic
|
||||
Smart Placement automatically routes 1% of requests WITHOUT optimization as baseline for performance comparison.
|
||||
|
||||
### Validation Rules
|
||||
|
||||
**Mutually exclusive fields:**
|
||||
- `mode` cannot be used with explicit placement fields (`region`, `host`, `hostname`)
|
||||
- Choose either Smart Placement OR explicit placement, not both
|
||||
|
||||
```jsonc
|
||||
// ✅ Valid - Smart Placement
|
||||
{ "placement": { "mode": "smart" } }
|
||||
|
||||
// ✅ Valid - Explicit Placement (different feature)
|
||||
{ "placement": { "region": "us-east1" } }
|
||||
|
||||
// ❌ Invalid - Cannot combine
|
||||
{ "placement": { "mode": "smart", "region": "us-east1" } }
|
||||
```
|
||||
|
||||
## Dashboard Configuration
|
||||
|
||||
**Workers & Pages** → Select Worker → **Settings** → **General** → **Placement: Smart** → Wait 15min → Check **Metrics**
|
||||
|
||||
## TypeScript Types
|
||||
|
||||
```typescript
|
||||
interface Env {
|
||||
BACKEND: Fetcher;
|
||||
DATABASE: D1Database;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const data = await env.DATABASE.prepare('SELECT * FROM table').all();
|
||||
return Response.json(data);
|
||||
}
|
||||
} satisfies ExportedHandler<Env>;
|
||||
```
|
||||
|
||||
## Cloudflare Pages/Assets Warning
|
||||
|
||||
**CRITICAL PERFORMANCE ISSUE:** Enabling Smart Placement with `assets.run_worker_first = true` in Pages projects **severely degrades asset serving performance**. This is one of the most common misconfigurations.
|
||||
|
||||
**Why this is bad:**
|
||||
- Smart Placement routes ALL requests (including static assets) away from edge to remote locations
|
||||
- Static assets (HTML, CSS, JS, images) should ALWAYS be served from edge closest to user
|
||||
- Result: 2-5x slower asset loading times, poor user experience
|
||||
|
||||
**Problem:** Smart Placement routes asset requests away from edge, but static assets should always be served from edge closest to user.
|
||||
|
||||
**Solutions (in order of preference):**
|
||||
1. **Recommended:** Split into separate Workers (frontend at edge + backend with Smart Placement)
|
||||
2. Set `"mode": "off"` to explicitly disable Smart Placement for Pages/Assets Workers
|
||||
3. Use `assets.run_worker_first = false` (serves assets first, bypasses Worker for static content)
|
||||
|
||||
```jsonc
|
||||
// ❌ BAD - Degrades asset performance by 2-5x
|
||||
{
|
||||
"name": "pages-app",
|
||||
"placement": { "mode": "smart" },
|
||||
"assets": { "run_worker_first": true }
|
||||
}
|
||||
|
||||
// ✅ GOOD - Frontend at edge, backend optimized
|
||||
// frontend-worker/wrangler.jsonc
|
||||
{
|
||||
"name": "frontend",
|
||||
"assets": { "run_worker_first": true }
|
||||
// No placement - runs at edge
|
||||
}
|
||||
|
||||
// backend-worker/wrangler.jsonc
|
||||
{
|
||||
"name": "backend-api",
|
||||
"placement": { "mode": "smart" },
|
||||
"d1_databases": [{ "binding": "DB", "database_id": "xxx" }]
|
||||
}
|
||||
```
|
||||
|
||||
**Key takeaway:** Never enable Smart Placement on Workers that serve static assets with `run_worker_first = true`.
|
||||
|
||||
## Local Development
|
||||
|
||||
Smart Placement does NOT work in `wrangler dev` (local only). Test by deploying: `wrangler deploy --env staging`
|
||||
@@ -0,0 +1,174 @@
|
||||
# Smart Placement Gotchas
|
||||
|
||||
## Common Errors
|
||||
|
||||
### "INSUFFICIENT_INVOCATIONS"
|
||||
|
||||
**Cause:** Not enough traffic for Smart Placement to analyze
|
||||
**Solution:**
|
||||
- Ensure Worker receives consistent global traffic
|
||||
- Wait longer (analysis takes up to 15 minutes)
|
||||
- Send test traffic from multiple global locations
|
||||
- Check Worker has fetch event handler
|
||||
|
||||
### "UNSUPPORTED_APPLICATION"
|
||||
|
||||
**Cause:** Smart Placement made Worker slower rather than faster
|
||||
**Reasons:**
|
||||
- Worker doesn't make backend calls (runs faster at edge)
|
||||
- Backend calls are cached (network latency to user more important)
|
||||
- Backend service has good global distribution
|
||||
- Worker serves static assets or Pages content
|
||||
|
||||
**Solutions:**
|
||||
- Disable Smart Placement: `{ "placement": { "mode": "off" } }`
|
||||
- Review whether Worker actually benefits from Smart Placement
|
||||
- Consider caching strategy to reduce backend calls
|
||||
- For Pages/Assets Workers, use separate backend Worker with Smart Placement
|
||||
|
||||
### "No request duration metrics"
|
||||
|
||||
**Cause:** Smart Placement not enabled, insufficient time passed, insufficient traffic, or analysis incomplete
|
||||
**Solution:**
|
||||
- Ensure Smart Placement enabled in config
|
||||
- Wait 15+ minutes after deployment
|
||||
- Verify Worker has sufficient traffic
|
||||
- Check `placement_status` is `SUCCESS`
|
||||
|
||||
### "cf-placement header missing"
|
||||
|
||||
**Cause:** Smart Placement not enabled, beta feature removed, or Worker not analyzed yet
|
||||
**Solution:** Verify Smart Placement enabled, wait for analysis (15min), check if beta feature still available
|
||||
|
||||
## Pages/Assets + Smart Placement Performance Degradation
|
||||
|
||||
**Problem:** Static assets load 2-5x slower when Smart Placement enabled with `run_worker_first = true`.
|
||||
|
||||
**Cause:** Smart Placement routes ALL requests (including static assets like HTML, CSS, JS, images) to remote locations. Static content should ALWAYS be served from edge closest to user.
|
||||
|
||||
**Solution:** Split into separate Workers OR disable Smart Placement:
|
||||
```jsonc
|
||||
// ❌ BAD - Assets routed away from user
|
||||
{
|
||||
"name": "pages-app",
|
||||
"placement": { "mode": "smart" },
|
||||
"assets": { "run_worker_first": true }
|
||||
}
|
||||
|
||||
// ✅ GOOD - Assets at edge, API optimized
|
||||
// frontend/wrangler.jsonc
|
||||
{
|
||||
"name": "frontend",
|
||||
"assets": { "run_worker_first": true }
|
||||
// No placement field - stays at edge
|
||||
}
|
||||
|
||||
// backend/wrangler.jsonc
|
||||
{
|
||||
"name": "backend-api",
|
||||
"placement": { "mode": "smart" }
|
||||
}
|
||||
```
|
||||
|
||||
This is one of the most common and impactful Smart Placement misconfigurations.
|
||||
|
||||
## Monolithic Full-Stack Worker
|
||||
|
||||
**Problem:** Frontend and backend logic in single Worker with Smart Placement enabled.
|
||||
|
||||
**Cause:** Smart Placement optimizes for backend latency but increases user-facing response time.
|
||||
|
||||
**Solution:** Split into two Workers:
|
||||
```jsonc
|
||||
// frontend/wrangler.jsonc
|
||||
{
|
||||
"name": "frontend",
|
||||
"placement": { "mode": "off" }, // Explicit: stay at edge
|
||||
"services": [{ "binding": "BACKEND", "service": "backend-api" }]
|
||||
}
|
||||
|
||||
// backend/wrangler.jsonc
|
||||
{
|
||||
"name": "backend-api",
|
||||
"placement": { "mode": "smart" },
|
||||
"d1_databases": [{ "binding": "DB", "database_id": "xxx" }]
|
||||
}
|
||||
```
|
||||
|
||||
## Local Development Confusion
|
||||
|
||||
**Issue:** Smart Placement doesn't work in `wrangler dev`.
|
||||
|
||||
**Explanation:** Smart Placement only activates in production deployments, not local development.
|
||||
|
||||
**Solution:** Test Smart Placement in staging environment: `wrangler deploy --env staging`
|
||||
|
||||
## Baseline Traffic & Analysis Time
|
||||
|
||||
**Note:** Smart Placement routes 1% of requests WITHOUT optimization for comparison (expected).
|
||||
|
||||
**Analysis time:** Up to 15 minutes. During analysis, Worker runs at edge. Monitor `placement_status`.
|
||||
|
||||
## RPC Methods Not Affected (Critical Limitation)
|
||||
|
||||
**Problem:** Enabled Smart Placement on backend but RPC calls still slow.
|
||||
|
||||
**Cause:** Smart Placement ONLY affects `fetch` handlers. RPC methods (Service Bindings with `WorkerEntrypoint`) are NEVER affected.
|
||||
|
||||
**Why:** RPC bypasses `fetch` handler - Smart Placement can only route `fetch` requests.
|
||||
|
||||
**Solution:** Convert to fetch-based Service Bindings:
|
||||
|
||||
```typescript
|
||||
// ❌ RPC - Smart Placement has NO EFFECT
|
||||
export class BackendRPC extends WorkerEntrypoint {
|
||||
async getData() {
|
||||
// ALWAYS runs at edge
|
||||
return await this.env.DATABASE.prepare('SELECT * FROM table').all();
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Fetch - Smart Placement WORKS
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
// Runs close to DATABASE when Smart Placement enabled
|
||||
const data = await env.DATABASE.prepare('SELECT * FROM table').all();
|
||||
return Response.json(data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- **Wrangler 2.20.0+** required
|
||||
- **Consistent multi-region traffic** needed for analysis
|
||||
- **Only affects fetch handlers** - RPC methods and named entrypoints not affected
|
||||
|
||||
## Limits
|
||||
|
||||
| Resource/Limit | Value | Notes |
|
||||
|----------------|-------|-------|
|
||||
| Analysis time | Up to 15 minutes | After enabling |
|
||||
| Baseline traffic | 1% | Routed without optimization |
|
||||
| Min Wrangler version | 2.20.0+ | Required |
|
||||
| Traffic requirement | Multi-region | Consistent needed |
|
||||
|
||||
## Disabling Smart Placement
|
||||
|
||||
```jsonc
|
||||
{ "placement": { "mode": "off" } } // Explicit disable
|
||||
// OR remove "placement" field entirely (same effect)
|
||||
```
|
||||
|
||||
Both behaviors identical - Worker runs at edge closest to user.
|
||||
|
||||
## When NOT to Use Smart Placement
|
||||
|
||||
- Workers serving only static content or cached responses
|
||||
- Workers without significant backend communication
|
||||
- Pure edge logic (auth checks, redirects, simple transformations)
|
||||
- Workers without fetch event handlers
|
||||
- Pages/Assets Workers with `run_worker_first = true`
|
||||
- Workers using RPC methods instead of fetch handlers
|
||||
|
||||
These scenarios won't benefit and may perform worse with Smart Placement.
|
||||
@@ -0,0 +1,183 @@
|
||||
# Smart Placement Patterns
|
||||
|
||||
## Backend Worker with Database Access
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const user = await env.DATABASE.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first();
|
||||
const orders = await env.DATABASE.prepare('SELECT * FROM orders WHERE user_id = ?').bind(userId).all();
|
||||
return Response.json({ user, orders });
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsonc
|
||||
{ "placement": { "mode": "smart" }, "d1_databases": [{ "binding": "DATABASE", "database_id": "xxx" }] }
|
||||
```
|
||||
|
||||
## Frontend + Backend Split (Service Bindings)
|
||||
|
||||
**Frontend:** Runs at edge for fast user response
|
||||
**Backend:** Smart Placement runs close to database
|
||||
|
||||
```typescript
|
||||
// Frontend Worker - routes requests to backend
|
||||
interface Env {
|
||||
BACKEND: Fetcher; // Service Binding to backend Worker
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
if (new URL(request.url).pathname.startsWith('/api/')) {
|
||||
return env.BACKEND.fetch(request); // Forward to backend
|
||||
}
|
||||
return new Response('Frontend content');
|
||||
}
|
||||
};
|
||||
|
||||
// Backend Worker - database operations
|
||||
interface BackendEnv {
|
||||
DATABASE: D1Database;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: BackendEnv): Promise<Response> {
|
||||
const data = await env.DATABASE.prepare('SELECT * FROM table').all();
|
||||
return Response.json(data);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**CRITICAL:** Use fetch-based Service Bindings (shown above). If using RPC with `WorkerEntrypoint`, Smart Placement will NOT optimize those method calls - only `fetch` handlers are affected.
|
||||
|
||||
**RPC vs Fetch - CRITICAL:** Smart Placement ONLY works with fetch-based bindings, NOT RPC.
|
||||
|
||||
```typescript
|
||||
// ❌ RPC - Smart Placement has NO EFFECT on backend RPC methods
|
||||
export class BackendRPC extends WorkerEntrypoint {
|
||||
async getData() {
|
||||
// ALWAYS runs at edge, Smart Placement ignored
|
||||
return await this.env.DATABASE.prepare('SELECT * FROM table').all();
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Fetch - Smart Placement WORKS
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
// Runs close to DATABASE when Smart Placement enabled
|
||||
const data = await env.DATABASE.prepare('SELECT * FROM table').all();
|
||||
return Response.json(data);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## External API Integration
|
||||
|
||||
```typescript
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const apiUrl = 'https://api.partner.com';
|
||||
const headers = { 'Authorization': `Bearer ${env.API_KEY}` };
|
||||
|
||||
const [profile, transactions] = await Promise.all([
|
||||
fetch(`${apiUrl}/profile`, { headers }),
|
||||
fetch(`${apiUrl}/transactions`, { headers })
|
||||
]);
|
||||
|
||||
return Response.json({
|
||||
profile: await profile.json(),
|
||||
transactions: await transactions.json()
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## SSR / API Gateway Pattern
|
||||
|
||||
```typescript
|
||||
// Frontend (edge) - auth/routing close to user
|
||||
export default {
|
||||
async fetch(request: Request, env: Env) {
|
||||
if (!request.headers.get('Authorization')) {
|
||||
return new Response('Unauthorized', { status: 401 });
|
||||
}
|
||||
const data = await env.BACKEND.fetch(request);
|
||||
return new Response(renderPage(await data.json()), {
|
||||
headers: { 'Content-Type': 'text/html' }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Backend (Smart Placement) - DB operations close to data
|
||||
export default {
|
||||
async fetch(request: Request, env: Env) {
|
||||
const data = await env.DATABASE.prepare('SELECT * FROM pages WHERE id = ?').bind(pageId).first();
|
||||
return Response.json(data);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Durable Objects with Smart Placement
|
||||
|
||||
**Key principle:** Smart Placement does NOT control WHERE Durable Objects run. DOs always run in their designated region (based on jurisdiction or smart location hints).
|
||||
|
||||
**What Smart Placement DOES affect:** The location of the coordinator Worker's `fetch` handler that makes calls to multiple DOs.
|
||||
|
||||
**Pattern:** Enable Smart Placement on coordinator Worker that aggregates data from multiple DOs:
|
||||
|
||||
```typescript
|
||||
// Worker with Smart Placement - aggregates data from multiple DOs
|
||||
export default {
|
||||
async fetch(request: Request, env: Env): Promise<Response> {
|
||||
const userId = new URL(request.url).searchParams.get('user');
|
||||
|
||||
// Get DO stubs
|
||||
const userDO = env.USER_DO.get(env.USER_DO.idFromName(userId));
|
||||
const analyticsID = env.ANALYTICS_DO.idFromName(`analytics-${userId}`);
|
||||
const analyticsDO = env.ANALYTICS_DO.get(analyticsID);
|
||||
|
||||
// Fetch from multiple DOs
|
||||
const [userData, analyticsData] = await Promise.all([
|
||||
userDO.fetch(new Request('https://do/profile')),
|
||||
analyticsDO.fetch(new Request('https://do/stats'))
|
||||
]);
|
||||
|
||||
return Response.json({
|
||||
user: await userData.json(),
|
||||
analytics: await analyticsData.json()
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
```jsonc
|
||||
// wrangler.jsonc
|
||||
{
|
||||
"placement": { "mode": "smart" },
|
||||
"durable_objects": {
|
||||
"bindings": [
|
||||
{ "name": "USER_DO", "class_name": "UserDO" },
|
||||
{ "name": "ANALYTICS_DO", "class_name": "AnalyticsDO" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**When this helps:**
|
||||
- Worker's `fetch` handler runs closer to DO regions, reducing network latency for multiple DO calls
|
||||
- Most beneficial when DOs are geographically concentrated or in specific jurisdictions
|
||||
- Helps when coordinator makes many sequential or parallel DO calls
|
||||
|
||||
**When this DOESN'T help:**
|
||||
- DOs are globally distributed (no single optimal Worker location)
|
||||
- Worker only calls a single DO
|
||||
- DO calls are infrequent or cached
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Split full-stack apps: frontend at edge, backend with Smart Placement
|
||||
- Use fetch-based Service Bindings (not RPC)
|
||||
- Enable for backend logic: APIs, data aggregation, DB operations
|
||||
- Don't enable for: static content, edge logic, RPC methods, Pages with `run_worker_first`
|
||||
- Wait 15+ min for analysis, verify `placement_status = SUCCESS`
|
||||
Reference in New Issue
Block a user