用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/skeletorflet/opencode-kit --skill background-jobs命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Web accessibility (a11y). WCAG 2.1, ARIA, keyboard navigation, screen readers, testing tools.
Analytics and event tracking. Product analytics, Mixpanel, PostHog, Segment, GDPR compliance, event taxonomy.
UI animation patterns. CSS transitions, Framer Motion, GSAP, performance, accessibility.
基于 SOC 职业分类
正在显示 SKILL.md
| name | background-jobs |
| description | Background job patterns. Cron jobs, queued jobs, scheduling, BullMQ, pg-boss, Sidekiq patterns. |
Move slow, unreliable, or heavy work out of the request lifecycle.
| Type | Description | Examples |
|---|---|---|
| Queued | Triggered by event, run ASAP | Send email, process image |
| Scheduled (cron) | Run on time interval | Daily report, cleanup |
| Delayed | Run after a delay | Trial expiry reminder |
| Recurring | Repeating job per user | Weekly digest |
| Chained | Job triggers next job | Pipeline: upload → resize → notify |
| Batch | Process many items | Bulk export, bulk email |
// queues/index.ts
import { Queue } from "bullmq";
import { redis } from "@/lib/redis";
const defaultJobOptions = {
attempts: 3,
backoff: { type: "exponential" as const, delay: 2000 },
removeOnComplete: { count: 100, age: 24 * 3600 },
removeOnFail: { count: 500 },
};
export const emailQueue = new Queue("emails", {
connection: redis,
defaultJobOptions,
});
export const imageQueue = new Queue("images", {
connection: redis,
defaultJobOptions: { ...defaultJobOptions, attempts: 2 },
});
// workers/email.worker.ts
import { Worker, Job } from "bullmq";
const worker = new Worker("emails", async (job: Job) => {
switch (job.name) {
case "welcome": return await sendWelcomeEmail(job.data);
case "invoice": return await sendInvoiceEmail(job.data);
case "reset": return await sendPasswordReset(job.data);
default: throw new Error(`Unknown job: ${job.name}`);
}
}, {
connection: redis,
concurrency: 5,
limiter: { max: 100, duration: 60_000 }, // 100 jobs/min
});
worker.on("completed", (job) => logger.info(`Job ${job.id} done`));
worker.on("failed", (job, err) => logger.error({ job: job?.id, err }, "Job failed"));
import { QueueScheduler, Queue } from "bullmq";
const reportQueue = new Queue("reports", { connection: redis });
// Add repeatable job
await reportQueue.add("daily-report", { type: "daily" }, {
repeat: { pattern: "0 9 * * *", tz: "America/New_York" }, // 9am daily
});
// One-time delayed
await reportQueue.add("trial-expiry-reminder", { userId }, {
delay: 13 * 24 * 60 * 60 * 1000, // 13 days
});
┌──────── minute (0–59)
│ ┌────── hour (0–23)
│ │ ┌──── day of month (1–31)
│ │ │ ┌── month (1–12)
│ │ │ │ ┌ day of week (0–6, Sun=0)
│ │ │ │ │
* * * * *
Examples:
0 * * * * Every hour at :00
0 9 * * 1-5 Weekdays at 9am
*/15 * * * * Every 15 minutes
0 0 1 * * First day of month at midnight
0 2 * * * Every day at 2am
Design jobs to be:
├── Idempotent (safe to run twice)
├── Atomic (fully succeeds or fails cleanly)
├── Independent (no shared state between jobs)
├── Observable (log start, end, error)
└── Bounded (time limit / timeout)
Job size:
├── Small jobs (< 30s) → simpler error handling
├── Large jobs → break into smaller jobs
└── Never: infinite loops without exit condition
// Update job progress
const worker = new Worker("exports", async (job) => {
const items = await fetchItems(job.data.filter);
for (let i = 0; i < items.length; i++) {
await processItem(items[i]);
await job.updateProgress(Math.round((i + 1) / items.length * 100));
}
});
// Client: poll progress
const job = await exportQueue.getJob(jobId);
console.log(await job.progress); // 0–100
// Handle SIGTERM (Kubernetes, Heroku)
process.on("SIGTERM", async () => {
logger.info("SIGTERM received, closing workers...");
await worker.close(); // Wait for current job to finish
await redis.quit();
process.exit(0);
});
// Bull Board (UI dashboard)
import { createBullBoard } from "@bull-board/api";
import { BullMQAdapter } from "@bull-board/api/bullMQAdapter";
import { ExpressAdapter } from "@bull-board/express";
const serverAdapter = new ExpressAdapter();
createBullBoard({
queues: [new BullMQAdapter(emailQueue), new BullMQAdapter(imageQueue)],
serverAdapter,
});
app.use("/admin/queues", serverAdapter.getRouter());
| ❌ Don't | ✅ Do |
|---|---|
| HTTP requests in cron without retry | Queue-based with retry |
| Blocking the main thread | Always async |
| Jobs without timeout | Set job timeout |
| No monitoring | Bull Board + alerts |
| Non-idempotent jobs | Design for at-least-once |