resilience-engineering
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseResilience Engineering for Shopify Apps
Shopify应用的韧性工程
Shopify's API limit is a "Leaky Bucket". If you pour too much too fast, it overflows (429 Too Many Requests). Your app must handle this gracefully.
Shopify的API限制采用的是「漏桶算法」。如果请求速度过快、量过大,桶就会溢出,返回429 Too Many Requests错误。你的应用必须优雅地处理这种情况。
1. Handling Rate Limits (429)
1. 处理速率限制(429)
The "Retry-After" Header
"Retry-After"响应头
When Shopify returns a 429, they include a header (seconds to wait).
Retry-AfterImplementation (using or custom delay):
bottlenecktypescript
async function fetchWithRetry(url, options, retries = 3) {
try {
const res = await fetch(url, options);
if (res.status === 429) {
const wait = parseFloat(res.headers.get("Retry-After") || "1.0");
if (retries > 0) {
await new Promise(r => setTimeout(r, wait * 1000));
return fetchWithRetry(url, options, retries - 1);
}
}
return res;
} catch (err) {
// network error handling
}
}Note: The official client handles retries automatically if configured.
@shopify/shopify-api当Shopify返回429错误时,响应会附带头,值为需要等待的秒数。
Retry-After实现方案(使用或自定义延迟):
bottlenecktypescript
async function fetchWithRetry(url, options, retries = 3) {
try {
const res = await fetch(url, options);
if (res.status === 429) {
const wait = parseFloat(res.headers.get("Retry-After") || "1.0");
if (retries > 0) {
await new Promise(r => setTimeout(r, wait * 1000));
return fetchWithRetry(url, options, retries - 1);
}
}
return res;
} catch (err) {
// network error handling
}
}注意:官方的客户端如果完成相关配置,会自动处理重试逻辑。
@shopify/shopify-api2. Queues & Throttling
2. 队列与限流
For bulk operations (e.g., syncing 10,000 products), you cannot just loop and await.
对于批量操作(比如同步10000个商品),不能简单地循环执行await请求。
Using bottleneck
bottleneck使用bottleneck
bottleneckbash
npm install bottlenecktypescript
import Bottleneck from "bottleneck";
const limiter = new Bottleneck({
minTime: 500, // wait 500ms between requests (2 req/sec)
maxConcurrent: 5,
});
const products = await limiter.schedule(() => shopify.rest.Product.list({ ... }));bash
npm install bottlenecktypescript
import Bottleneck from "bottleneck";
const limiter = new Bottleneck({
minTime: 500, // wait 500ms between requests (2 req/sec)
maxConcurrent: 5,
});
const products = await limiter.schedule(() => shopify.rest.Product.list({ ... }));Background Jobs (BullMQ)
后台任务(BullMQ)
Move heavy lifting to a background worker. (See skill - to be added if needed, but conceptually here).
redis-bullmq将 heavy 任务转移到后台worker执行。(可参考技能——如有需要后续会添加,此处先介绍概念)。
redis-bullmq3. Circuit Breaker
3. 熔断机制
If an external service (e.g., your own backend API or a shipping carrier) goes down, stop calling it to prevent cascading failures.
如果外部服务(比如你自己的后端API或者物流商服务)宕机,要停止调用该服务,避免发生级联故障。
Using cockatiel
cockatiel使用cockatiel
cockatielbash
npm install cockatieltypescript
import { CircuitBreaker, handleAll, retry } from 'cockatiel';
// Create a Retry Policy
const retryPolicy = retry(handleAll, { maxAttempts: 3, backoff: new ExponentialBackoff() });
// Create a Circuit Breaker (open after 5 failures, reset after 10s)
const circuitBreaker = new CircuitBreaker(handleAll, {
halfOpenAfter: 10 * 1000,
breaker: new ConsecutiveBreaker(5),
});
// Execute
const result = await retryPolicy.execute(() =>
circuitBreaker.execute(() => fetchMyService())
);bash
npm install cockatieltypescript
import { CircuitBreaker, handleAll, retry } from 'cockatiel';
// Create a Retry Policy
const retryPolicy = retry(handleAll, { maxAttempts: 3, backoff: new ExponentialBackoff() });
// Create a Circuit Breaker (open after 5 failures, reset after 10s)
const circuitBreaker = new CircuitBreaker(handleAll, {
halfOpenAfter: 10 * 1000,
breaker: new ConsecutiveBreaker(5),
});
// Execute
const result = await retryPolicy.execute(() =>
circuitBreaker.execute(() => fetchMyService())
);4. Webhook Idempotency
4. Webhook幂等性
Shopify guarantees "at least once" delivery. You might receive the same webhook twice.
Fix: Store in Redis/DB with a short TTL (e.g., 24h). If it exists, ignore the request.
orders/createX-Shopify-Webhook-IdShopify保证Webhook「至少投递一次」,你可能会两次收到同一个 webhook请求。
解决方案: 将存储在Redis/数据库中,设置较短的TTL(比如24小时)。如果请求携带的ID已经存在,直接忽略该请求。
orders/createX-Shopify-Webhook-Id