用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Dev-Toolbelt/dev-team-agents --skill async-jobs命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | async-jobs |
| description | Async jobs — idempotency, retry strategy, DLQ, observability. |
queue, worker, job, or consumer directories or files in the projectlaravel/horizon, sidekiq, celery, bullmq, bee-queue, faktory, resque, delayed_jobaws-sdk with SQS usage, @google-cloud/pubsub, Azure Service Bus, RabbitMQ, KafkaQUEUE_* / REDIS_QUEUE_* / SQS_* env varsapp/Console/Kernel.php, celerybeat, node-cron, whenever gemUse a background job when:
Keep it synchronous when:
Every job must be safe to run more than once with the same payload.
Queues guarantee at-least-once delivery — a job will be retried on failure and may execute multiple times. A non-idempotent job causes duplicate records, double charges, or double notifications.
# ❌ Non-idempotent — creates a duplicate on retry
def send_welcome_email(user_id: str):
user = User.find(user_id)
Email.send(to=user.email, template="welcome")
# ✅ Idempotent — checks state before acting
def send_welcome_email(user_id: str):
user = User.find(user_id)
if user.welcome_email_sent_at is not None:
return # already sent — safe to skip
Email.send(to=user.email, template="welcome")
user.update(welcome_email_sent_at=now())
Idempotency patterns:
(job_type, entity_id) to prevent duplicate executionTreat job payloads with the same rigor as HTTP input — validate before processing:
// ✅ Validate payload shape before using it
async handle(payload: unknown) {
const { orderId, userId } = validateOrderPayload(payload); // throws on invalid
const order = await Order.findOrFail(orderId);
// ...
}
Rules:
| Failure type | Action |
|---|---|
| Transient (network timeout, 503) | Retry with exponential backoff |
| Business rule failure (record not found yet) | Retry with delay |
| Validation failure (malformed payload) | Fail permanently — do not retry |
| Unexpected exception | Retry up to max attempts, then DLQ |
Exponential backoff formula: base_delay * 2^attempt with jitter — prevents thundering herd when many jobs fail simultaneously.
Minimum retry baseline — unless the project defines otherwise: 3 attempts with exponential backoff starting at 2 s (2s → 4s → 8s), then DLQ.
Configure per-job retry limits (not global defaults) — a payment job and an analytics job have different tolerance for retries. A payment job warrants fewer attempts and faster DLQ escalation than an analytics event.
Every queue must have a DLQ configured. Jobs that exhaust retries move there instead of being silently dropped.
After moving to DLQ:
Never delete DLQ messages without inspecting them — they contain the data of failed operations that may need manual resolution.
// TypeScript / BullMQ example — same principles apply in any framework
class ProcessOrderJob {
static queue = 'orders';
static attempts = 3;
static backoff = { type: 'exponential', delay: 2000 };
async handle(payload: ProcessOrderPayload): Promise<void> {
// 1. Validate payload
const { orderId } = validateProcessOrderPayload(payload);
// 2. Idempotency check
const order = await Order.findOrFail(orderId);
if (order.status !== OrderStatus.PENDING) return;
// 3. Execute in a transaction if multiple writes are involved
await db.transaction(async (trx) => {
await order.process({ trx });
await Inventory.reserve(order.items, { trx });
});
// 4. Trigger downstream jobs (don't chain synchronously)
await SendOrderConfirmationJob.dispatch({ orderId });
}
}
# ✅ Structured log with context
logger.info("order.processed", extra={
"job_id": self.request.id,
"order_id": order_id,
"duration_ms": elapsed,
})
| Framework | Key consideration |
|---|---|
| Laravel Queue | Use ShouldBeUnique for deduplication; ShouldBeIdempotent pattern via uniqueId(); Horizon for monitoring |
| Sidekiq / ActiveJob | Use sidekiq-unique-jobs for deduplication; set retry and dead thresholds per worker class |
| Celery | Set acks_late=True for at-least-once; use task_id for idempotency; Flower for monitoring |
| BullMQ | Use jobId option for deduplication; configure removeOnComplete to avoid memory bloat |
| AWS SQS | Visibility timeout must exceed max job duration; use MessageDeduplicationId on FIFO queues |
| RabbitMQ | Set acks_late / manual ack — never auto-ack before processing completes; configure a dead-letter exchange (x-dead-letter-exchange) per queue; use x-message-ttl to cap retry window; prefer quorum queues over classic for durability |
| Apache Kafka | Idempotency is consumer-side — commit offset only after successful processing (enable.auto.commit=false); use a unique group.id per consumer app; store processed offsets or business keys to detect replays; DLQ = a dedicated topic (e.g. topic.DLT); monitor consumer lag as the primary health signal |