agentsclimarketplace

Background jobs

Skill jacob-balslev/skills/skills/backend-engineering/background-jobs

Public Agent Skills library exported from skill-graph. Install: npx skills add jacob-balslev/skills

Install
npx -y skills add jacob-balslev/skills --skill background-jobs

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Use when moving slow or failure-prone work out of a request path, designing job queues, retries, checkpoints, progress reporting, cancellation, or worker concurrency. Covers inline-vs-background decisions, queue contracts, state machines, idempotency, retry/backoff, progress signals, worker leases, and user-visible completion reporting. Do NOT use for time-based schedule design (use `cron-scheduling`), live browser transport choice (use `real-time-updates`), or async message schema ownership (use `event-contract-design`).

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

23.1 KB, as published. Nobody here has run it

Background Jobs

Concept of the skill

A background job system has five primitives: a producer records durable work, a queue orders and deduplicates it, a worker leases and executes it, a state store records progress and outcomes, and a notification path tells humans or systems what changed.

Coverage

  • Inline-vs-background execution decisions for web requests, API routes, workers, and serverless functions.
  • Durable job contracts: job identity, payload, state, priority, ownership, attempts, progress, result, and failure record.
  • Queue and worker patterns: push queues, pull queues, leases, deduplication, priority, rate control, and concurrency limits.
  • Reliability patterns: idempotency keys, retry classification, exponential backoff with jitter, checkpoints, dead-letter handling, and partial failure recovery.
  • User-visible progress: stage names, percentages, timestamps, cancellation, completion notification, and stale status handling.
  • Verification: proving long work left the request path without losing observability or recovery paths.

Philosophy of the skill

Background jobs are not just "run this later." They are a reliability boundary between interactive work and processing work. A request handler is optimized for short, synchronous feedback. A worker is optimized for durable execution, retries, checkpoints, and controlled resource use.

The most common failure is moving code into a worker while keeping request-handler assumptions: no durable state, no idempotency, no progress, no cancellation, and no evidence of completion. That makes the system feel faster only until the first timeout, duplicate enqueue, or partial failure. A good job design makes the execution contract visible before choosing a queue product.

Execution Decision Gate

Use this gate before adding a queue. If any answer in the right column is true, design a background job instead of inline request work.

QuestionInline request is acceptable whenBackground job is required when
DurationWork predictably completes within a few secondsWork can exceed the request budget or has unbounded input size
Failure shapeFailure is atomic and easy to show immediatelyFailure can be partial, transient, or recoverable
User feedbackThe caller needs the result before continuingThe caller can continue with progress or later completion
Retry safetyRetrying the request is harmless and visibleRetrying needs idempotency, checkpointing, or backoff
Resource useWork uses normal request resourcesWork may saturate CPU, memory, database connections, or external limits
CoordinationOne request owns the whole operationMany producers, workers, or duplicate triggers can touch the same work

Rule of thumb: if you need progress, checkpointing, retry classification, cancellation, concurrency control, or delayed completion, you are already designing a background job.

Job Contract

Every background job needs a durable contract. The exact storage can be a database row, queue message plus result table, workflow engine state, or object store record, but the same fields need clear ownership.

FieldPurposeFailure if omitted
Job IDStable handle for status, logs, and supportCannot find or correlate work after enqueue
TypeRoutes to the correct handlerWorkers need payload guessing or brittle branching
PayloadImmutable input to the handlerRetries run against changing state by accident
Idempotency keyDeduplicates repeated enqueue attemptsDuplicate processing and double side effects
StatusCommunicates lifecycle stateWork disappears into a black box
Attempts and max attemptsControls retry lifecycleInfinite retry loops or premature dead-lettering
ProgressShows percentage, count, stage, or checkpointHumans see a spinner with no useful signal
ResultRecords output or pointer to outputCompletion cannot be consumed reliably
Failure reasonRecords actionable failure classOperators see failed without knowing why
Lease or lockEnsures one worker owns an active jobTwo workers process the same job concurrently

State Machine

Keep states few and explicit:

queued -> running -> succeeded
                  -> retry_waiting -> running
                  -> failed
                  -> cancelled

Use failed for terminal failure after retry policy is exhausted. Use cancelled only when the system intentionally stops work. Do not collapse retryable and terminal failures into one ambiguous error state.

Queue And Worker Patterns

PatternUse whenWatch out for
Database-backed queueYou need simple durability near app data and moderate volumePolling cadence, lock contention, cleanup of old rows
Managed queueYou need high throughput, delayed retry, and dead-letter supportMessage visibility timeouts and at-least-once delivery
Workflow engineYou need multi-step orchestration, step retries, or human-visible tracesVendor lock-in and over-modeling simple jobs
In-process workerYou need low-latency local processing in a persistent serviceProcess restarts lose work unless the queue is durable
Fire-and-forget taskWork is non-critical and safe to loseMost product work is not actually safe to lose

Lease-Based Pull Worker

Use a lease when workers pull from a shared store. The lease prevents two workers from processing the same job while still letting another worker recover abandoned work after the lease expires.

async function claimNextJob(workerId: string) {
  return updateOneJob(
    {
      status: "queued",
      runAfter: { lte: new Date() },
    },
    {
      status: "running",
      leaseOwner: workerId,
      leaseExpiresAt: new Date(Date.now() + 5 * 60 * 1000),
      startedAt: new Date(),
    },
    { sort: { priority: 1, createdAt: 1 } },
  );
}

Reliability Patterns

Idempotency

Background workers must assume at-least-once execution. A retry, duplicate enqueue, worker crash, or lease expiry can run the same logical job more than once.

Use an idempotency key that represents the logical work, not the physical attempt:

const idempotencyKey = `report:${workspaceId}:${periodStart}:${periodEnd}`;

await enqueueJob({
  type: "report.generate",
  idempotencyKey,
  payload: { workspaceId, periodStart, periodEnd },
});

The worker should also make side effects idempotent. Deduplicating enqueue is helpful but not sufficient because messages can be delivered more than once.

Retry Classification

Not every failure deserves a retry.

Failure classRetry?Handling
Transient network or service unavailableYesExponential backoff with jitter
Rate limitedYesRespect retry-after signals when available
Validation errorNoMark terminal failure and expose the fixable input issue
Missing permissionNoMark terminal failure and request operator action
Partial progressYesResume from checkpoint instead of restarting
Unknown failureLimitedRetry a small number of times, then dead-letter with context

Use jitter so a shared outage does not cause every worker to retry at the same moment:

function retryDelayMs(attempt: number) {
  const base = 1000;
  const cap = 5 * 60 * 1000;
  const exponential = Math.min(base * 2 ** attempt, cap);
  const jitter = Math.floor(Math.random() * base);
  return exponential + jitter;
}

Checkpointing

Long jobs need resumable checkpoints. A checkpoint should identify the last committed unit of work, not just a percentage.

async function processPages(jobId: string) {
  let cursor = await loadCheckpoint(jobId);

  while (true) {
    const page = await fetchNextPage(cursor);
    if (page.items.length === 0) break;

    await processBatch(page.items);
    cursor = page.nextCursor;
    await saveCheckpoint(jobId, cursor);
    await updateProgress(jobId, { stage: "processing", processed: page.totalProcessed });
  }
}

Progress Throttling

Progress writes are product value only when they communicate meaningful change. Updating progress after every item in a large batch can overload the same database or cache the job is trying to use.

Use one of these gates:

  • Update every N items.
  • Update every T seconds.
  • Update when the stage changes.
  • Update at completion or terminal failure.

User-Facing Progress

The UI does not need internal worker details. It needs a stable status contract.

DurationProgress contractUX expectation
Under 5 secondsPending state onlyInline spinner or disabled action
5-30 secondsStatus plus short messageProgress bar or step label
30 seconds-5 minutesStatus, stage, count, and cancel option when safeDedicated progress panel or status row
Over 5 minutesDurable status page plus completion signalUser can leave and return later

Avoid fake precision. If you do not know the denominator, report stages or processed counts instead of a misleading percentage.

Concurrency And Priority

Concurrency limits protect shared resources. Define at least one limit before shipping a worker:

LimitProtectsExample
Global worker concurrencyCPU, memory, queue pressureMax 10 running jobs total
Per-workspace concurrencyFairness and duplicate workMax 1 import per workspace
Per-job-type concurrencyHot paths and external servicesMax 3 report renders
Rate limitExternal calls or expensive writesMax 100 requests per minute

Priority should reorder queued work, not bypass safety. A high-priority job still needs idempotency, leases, and retry policy.

Observability

Background jobs need enough telemetry to answer four questions without reading code:

  • Was the job enqueued?
  • Did a worker claim it?
  • What progress or checkpoint was last committed?
  • Did it succeed, fail terminally, retry, or get cancelled?

Log job ID, type, attempt number, state transitions, duration, failure class, and queue latency. Emit metrics for queue depth, age of oldest queued job, success rate, retry rate, terminal failure rate, and worker saturation. Trace multi-step jobs when a single user action fans out into several worker operations.

Verification

After applying this skill, verify:

  • Long or unbounded work is outside the interactive request path.
  • Every enqueued job has a durable status that can be queried after refresh or worker restart.
  • The job contract includes idempotency, attempts, progress, result, and failure reason.
  • Retry policy distinguishes transient, rate-limit, validation, permission, partial-progress, and unknown failures.
  • Backoff includes jitter or an equivalent herd-prevention mechanism.
  • Long jobs checkpoint the last committed unit of work.
  • Progress updates are throttled by item count, time, stage, or completion.
  • Worker concurrency is bounded globally and at any needed fairness boundary.
  • Terminal failure and cancellation are visible to users or operators.
  • Tests or manual probes cover duplicate enqueue, retry, resume, and terminal failure behavior.

Do NOT Use When

Use insteadWhen
cron-schedulingYou are choosing when recurring work starts, validating cron expressions, or preventing overlap in a scheduled trigger.
real-time-updatesYou are choosing polling, Server-Sent Events, or WebSocket transport for browser freshness.
event-contract-designYou are defining async event envelopes, topic names, replay semantics, or producer/consumer compatibility.
observability-modelingYou are designing telemetry vocabulary across logs, metrics, traces, and alerts without changing job execution behavior.
debuggingA deployed worker or queue is already failing and needs root-cause investigation.

Anti-Patterns

Anti-patternWhy it failsBetter pattern
Long work in a request handlerTimeouts and partial side effects are user-visible failuresEnqueue durable work and return a job ID
Fire-and-forget without a status recordNo one can tell whether work ran, failed, or is still pendingStore job state and expose status
Retrying every failureValidation and permission failures waste capacity and hide real action itemsClassify failures before retrying
Restarting from zero after partial progressRetries get slower and can duplicate side effectsSave checkpoints at committed boundaries
Unlimited workersShared resources get saturated during spikesBound concurrency and add leases
Progress update per itemProgress tracking becomes the bottleneckThrottle progress writes
Queue code owns domain rulesWorker infrastructure becomes hard to test and reuseKeep domain logic in services; workers orchestrate execution

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.