Advanced (अपडेट: 2/6/2026)

Claude Code से Queue System बनाना: Async Processing की Practical Guide

Producer, worker, retry, DLQ, idempotency और monitoring के साथ Claude Code queue design.

Claude Code से Queue System बनाना: Async Processing की Practical Guide

Claude Code से web application बनाते समय अक्सर हर काम HTTP request के अंदर डाल दिया जाता है। form submit हुआ तो उसी request में email भेज दिया, image upload हुई तो उसी समय resize कर दिया, payment webhook आया तो order update, invoice, email और CRM write सब एक साथ कर दिया। demo में यह ठीक लगता है, लेकिन production में timeout, duplicate request, deploy restart, provider rate limit और partial failure आते हैं।

Queue system इस समस्या को अलग तरीके से हल करता है। API सिर्फ काम स्वीकार करती है और job को queue में डालती है। worker बाद में job उठाता है, message payload पढ़ता है, काम करता है, success पर acknowledge करता है, temporary failure पर retry करता है और बार-बार fail होने वाले job को dead-letter queue यानी DLQ में भेजता है। Claude Code को prompt देते समय producer यानी job डालने वाला code, consumer यानी worker, visibility timeout यानी worker के पास job रहने तक दूसरे worker से छिपाने का समय, idempotency यानी duplicate job आने पर भी business result एक बार ही बनना, backpressure यानी capacity कम होने पर input धीमा करना, और monitoring यानी queue की हालत देखने वाले metrics साफ लिखने चाहिए।

इस guide के example dependency-free Node.js scripts हैं। इन्हें चलाने के लिए Redis, AWS या RabbitMQ की जरूरत नहीं है। पहले local script से behavior समझिए, फिर production में SQS, RabbitMQ, BullMQ या कोई और broker चुनिए।

Queue का mental model

Queue केवल “background में काम चलाना” नहीं है। यह external service को spike से बचाती है, slow काम को request से अलग करती है, failed job को देखने लायक बनाती है और worker concurrency को control करती है।

flowchart LR
  A["Producer<br/>API, cron, webhook"] --> B["Queue<br/>message payload"]
  B --> C["Consumer<br/>worker process"]
  C --> D["External service<br/>mail, image, billing"]
  C -- "retryable failure" --> B
  C -- "poison message" --> E["DLQ<br/>manual review"]
  C --> F["Metrics<br/>logs and alerts"]
Termआसान अर्थDesign decision
Producerजो code job को queue में डालता हैpayload shape, validation, priority, dedupe key
Consumerजो worker job उठाकर चलाता हैconcurrency, timeout, failure handling
Message payloadworker को मिलने वाला dataIDs, type, schemaVersion, secrets नहीं
Visibility timeoutjob उठाने के बाद छिपा रहने का समयp95 processing time से थोड़ा ज्यादा
Retrytemporary failure को फिर से चलानाmax attempts, backoff, jitter
DLQलगातार fail job की अलग queueowner, alert, replay rule
Idempotencyduplicate job पर भी result duplicate न होunique key, processed table
Backpressuresystem busy हो तो input धीमा करनाconcurrency limit, rate limit
Monitoringqueue healthy है या stuck, यह देखनाdepth, oldest job, failure rate, DLQ count

यह table Claude Code prompt में शामिल करें। इससे generated code में केवल happy path नहीं, बल्कि failure path भी दिखेगा।

Use cases

पहला use case email sending queue है। welcome email, password reset, invoice reminder और support reply request को block नहीं करने चाहिए। आगे के लिए email automation और SendGrid email guide देखें। payload में deliveryId, templateId, userId जैसे reference रखें; API key, token या पूरा email body नहीं।

दूसरा use case image/video processing है। thumbnail, WebP conversion, virus scan, subtitle और preview generation CPU heavy हो सकते हैं। Queue API को जल्दी “accepted” return करने देती है और worker concurrency limit करती है। गलती यह होती है कि upload बढ़ते ही unlimited workers चल जाते हैं।

तीसरा use case billing retry है। payment provider या card network temporary fail हो सकता है। Retry queue मदद करती है, लेकिन retry finite होना चाहिए। Idempotency key, exponential backoff, DLQ और manual review के बिना duplicate charge या rate limit incident हो सकता है।

चौथा use case lead enrichment और report generation है। form submit के बाद company enrichment, CRM write, sales report और Slack notification async चल सकते हैं। बड़ा design event-driven architecture, जांच logging and monitoring, और payload safety security best practices से जोड़कर बनाएं।

Example 1: dependency-free in-memory queue

इस file को queue-basic-demo.mjs नाम से save करें और node queue-basic-demo.mjs चलाएं। यह producer, consumer, payload, visibility timeout और backpressure दिखाता है। यह production queue नहीं है क्योंकि data memory में है, पर model समझने के लिए उपयोगी है।

// queue-basic-demo.mjs
let nextJobId = 1;

class InMemoryQueue {
  constructor({ visibilityTimeoutMs = 800, maxInFlight = 2 } = {}) {
    this.visibilityTimeoutMs = visibilityTimeoutMs;
    this.maxInFlight = maxInFlight;
    this.ready = [];
    this.inFlight = new Map();
  }

  enqueue(type, payload) {
    const job = {
      id: `job-${nextJobId++}`,
      type,
      payload,
      attempts: 0,
      visibleAt: 0,
      lockedBy: null,
    };
    this.ready.push(job);
    return job.id;
  }

  receive(workerId) {
    this.requeueExpired();

    if (this.inFlight.size >= this.maxInFlight) {
      return null;
    }

    const job = this.ready.shift();
    if (!job) return null;

    job.attempts += 1;
    job.lockedBy = workerId;
    job.visibleAt = Date.now() + this.visibilityTimeoutMs;
    this.inFlight.set(job.id, job);

    return {
      id: job.id,
      type: job.type,
      payload: job.payload,
      attempts: job.attempts,
    };
  }

  ack(jobId) {
    this.inFlight.delete(jobId);
  }

  requeueExpired(now = Date.now()) {
    for (const [jobId, job] of this.inFlight.entries()) {
      if (job.visibleAt <= now) {
        this.inFlight.delete(jobId);
        job.lockedBy = null;
        this.ready.push(job);
      }
    }
  }

  stats() {
    this.requeueExpired();
    return {
      ready: this.ready.length,
      inFlight: this.inFlight.size,
    };
  }
}

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

function produce(queue) {
  queue.enqueue("email.send", {
    deliveryId: "mail-1001",
    templateId: "welcome",
    userId: "user-42",
  });
  queue.enqueue("image.resize", {
    assetId: "asset-9001",
    sizes: [320, 768, 1280],
  });
  queue.enqueue("report.generate", {
    reportId: "weekly-2026-06-02",
    accountId: "acct-7",
  });
}

async function consume(queue, workerId) {
  for (let step = 0; step < 8; step += 1) {
    const job = queue.receive(workerId);

    if (!job) {
      console.log(`${workerId}: no job or backpressure`, queue.stats());
      await sleep(120);
      continue;
    }

    console.log(`${workerId}: started ${job.id}`, job.payload);
    await sleep(job.type === "image.resize" ? 300 : 90);
    queue.ack(job.id);
    console.log(`${workerId}: acked ${job.id}`, queue.stats());
  }
}

async function main() {
  const queue = new InMemoryQueue({
    visibilityTimeoutMs: 500,
    maxInFlight: 2,
  });

  produce(queue);
  await Promise.all([consume(queue, "worker-a"), consume(queue, "worker-b")]);
  console.log("final stats", queue.stats());
}

void main();

Production में ready array की जगह SQS, RabbitMQ, Redis या managed broker होगा। लेकिन state वही रहती है: ready, in-flight, acked, या timeout के बाद फिर से ready।

Example 2: worker idempotency guard

Queue आम तौर पर at-least-once delivery देती है। इसका मतलब job दोबारा आ सकता है। अगर worker idempotent नहीं है, तो email, payment, points या CRM entry duplicate हो सकते हैं।

// idempotent-worker-demo.mjs
const idempotencyStore = new Map();
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function withIdempotency(key, work) {
  const current = idempotencyStore.get(key);

  if (current?.status === "done") {
    return { skipped: true, result: current.result };
  }

  if (current?.status === "processing") {
    return { skipped: true, reason: "already processing" };
  }

  idempotencyStore.set(key, { status: "processing" });

  try {
    const result = await work();
    idempotencyStore.set(key, { status: "done", result });
    return { skipped: false, result };
  } catch (error) {
    idempotencyStore.delete(key);
    throw error;
  }
}

async function fakeSendEmail(payload) {
  await sleep(50);
  return {
    providerMessageId: `sg_${payload.deliveryId}`,
    sentToUserId: payload.userId,
  };
}

async function handleEmailJob(job) {
  const key = job.payload.idempotencyKey;
  if (!key) throw new Error("missing idempotencyKey");

  return withIdempotency(key, () => fakeSendEmail(job.payload));
}

async function main() {
  const original = {
    id: "job-1",
    payload: {
      idempotencyKey: "email:welcome:user-42",
      deliveryId: "mail-1001",
      userId: "user-42",
    },
  };

  console.log(await handleEmailJob(original));
  console.log(await handleEmailJob({ ...original, id: "job-1-redelivery" }));
}

void main();

Production में Map की जगह database unique constraint, Redis SETNX, या provider idempotency key इस्तेमाल करें। Claude Code को कहें कि external side effect success होने के बाद ही done mark करे, failure पर lock हटाए, और payload में secrets न रखे।

Example 3: retry और DLQ

Retry temporary error के लिए है। Invalid payload, deleted user, permission error या missing config retry से ठीक नहीं होते। ऐसे poison message को main queue में वापस भेजते रहना worker capacity बर्बाद करता है।

// retry-dlq-demo.mjs
let nextRetryJobId = 1;

class RetryQueue {
  constructor({ maxAttempts = 3 } = {}) {
    this.maxAttempts = maxAttempts;
    this.ready = [];
    this.delayed = [];
    this.dead = [];
    this.completed = [];
  }

  enqueue(payload) {
    this.ready.push({
      id: `retry-job-${nextRetryJobId++}`,
      payload,
      attempts: 0,
      runAt: Date.now(),
      lastError: null,
    });
  }

  moveReadyJobs(now = Date.now()) {
    const stillDelayed = [];
    for (const job of this.delayed) {
      if (job.runAt <= now) {
        this.ready.push(job);
      } else {
        stillDelayed.push(job);
      }
    }
    this.delayed = stillDelayed;
  }

  retryOrDeadLetter(job, error) {
    job.lastError = error.message;

    if (job.attempts >= this.maxAttempts) {
      this.dead.push(job);
      return;
    }

    const delayMs = 50 * 2 ** (job.attempts - 1);
    job.runAt = Date.now() + delayMs;
    this.delayed.push(job);
  }

  async drain(handler) {
    let idleRounds = 0;

    while (this.ready.length > 0 || this.delayed.length > 0) {
      this.moveReadyJobs();
      const job = this.ready.shift();

      if (!job) {
        idleRounds += 1;
        if (idleRounds > 100) throw new Error("drain timeout");
        await sleep(20);
        continue;
      }

      idleRounds = 0;
      job.attempts += 1;

      try {
        const result = await handler(job);
        this.completed.push({ id: job.id, result });
      } catch (error) {
        this.retryOrDeadLetter(job, error);
      }
    }

    return {
      completed: this.completed.length,
      dead: this.dead.length,
    };
  }
}

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function handler(job) {
  if (job.payload.kind === "poison") {
    throw new Error("invalid payload schema");
  }

  if (job.payload.kind === "flaky" && job.attempts < 2) {
    throw new Error("temporary provider timeout");
  }

  return `processed ${job.payload.kind}`;
}

async function main() {
  const queue = new RetryQueue({ maxAttempts: 3 });
  queue.enqueue({ kind: "normal" });
  queue.enqueue({ kind: "flaky" });
  queue.enqueue({ kind: "poison" });

  console.log(await queue.drain(handler));
  console.log(
    "dead letters",
    queue.dead.map((job) => ({
      id: job.id,
      attempts: job.attempts,
      lastError: job.lastError,
      payload: job.payload,
    }))
  );
}

void main();

DLQ तभी उपयोगी है जब कोई उसका owner हो। alert, failure reason, correction, replay rule और delete rule लिखना जरूरी है।

Operational checklist

  • payload में jobId, type, schemaVersion, business ID और idempotency key है।
  • payload में API key, OAuth token, card data, पूरा email body या sensitive personal data नहीं है।
  • producer enqueue से पहले payload validate करता है।
  • visibility timeout p95 processing time से थोड़ा ज्यादा है।
  • retry count, backoff, jitter और DLQ rule production से पहले तय हैं।
  • worker concurrency DB connection, provider rate limit, CPU और memory के हिसाब से है।
  • queue depth, oldest job age, active count, failure rate, DLQ count और p95 monitor होते हैं।
  • DLQ review, replay, delete और customer communication के लिए runbook है।
  • email, billing, points और CRM duplicate delivery मानकर बने हैं।
  • Claude Code review happy path के साथ failure path भी देखता है।

Visibility timeout बहुत छोटा होगा तो एक ही job दो worker चला सकते हैं। बहुत लंबा होगा तो worker crash के बाद job देर तक छिपा रहेगा। Real p95 मापें और लंबे job को छोटे steps में बांटें।

Claude Code prompt

Prompt में failure contract भी लिखें:

इस repository में email delivery queue जोड़ें। API request save करे और queue में सिर्फ deliveryId और templateId डाले। worker idempotency key से duplicate send रोके, temporary provider error को maximum 3 attempts exponential backoff के साथ retry करे, और repeated failure को DLQ table में भेजे। payload में API key, email body या personal data न रखें। queue depth, oldest job age, failure rate और DLQ count logs या metrics में दिखाएं। duplicate delivery, poison message और visibility timeout tests जोड़ें।

ऐसा prompt Claude Code को production-ready दिशा देता है, केवल demo नहीं।

Official docs और tool choice

AWS-first system के लिए Amazon SQS Developer Guide देखें। Routing, exchange और pub/sub चाहिए तो RabbitMQ documentation देखें। Node.js और Redis stack में delayed jobs, repeatable jobs और worker ergonomics चाहिए तो BullMQ documentation उपयोगी है।

Tool पहले न चुनें। Payload, idempotency, retry, DLQ, monitoring, permission, cost और team operation को पहले तय करें।

Common pitfalls

Duplicate processing पहला pitfall है। Queue exactly-once business effect नहीं देती। Ack loss, timeout, worker restart से job फिर आ सकता है।

Poison message दूसरा pitfall है। Broken schema, deleted user या permission error retry से ठीक नहीं होगा। Validate करें, reason save करें, DLQ भेजें और fix के बाद replay करें।

Infinite retry तीसरा pitfall है। Provider outage में immediate retry traffic बढ़ाता है। Finite attempts, backoff, jitter और backpressure लगाएं।

Secrets in payload चौथा pitfall है। Queue data logs, DLQ, dashboard और support tools में दिख सकता है। Payload में reference IDs रखें और worker authorized store से sensitive data पढ़े।

Training और consulting

Queue code छोटा दिखता है, लेकिन operation मुश्किल होता है। ClaudeCodeLab टीमों के लिए Claude Code prompts, CLAUDE.md rules, payload schema, DLQ runbook, metrics और CI review rules बना सकता है। Team rollout के लिए Claude Code training and consulting देखें। Solo work में इस checklist को PR template में डालना भी बड़ा सुधार है।

Summary

Job queue production infrastructure है। यह slow work control करती है, failure isolate करती है, duplicate business effect रोकती है, concurrency limit करती है और investigation के लिए evidence छोड़ती है। Claude Code से queue बनवाते समय producer, consumer, payload, visibility timeout, retry, DLQ, idempotency, backpressure और monitoring पहले prompt में लिखें।

Masa का hands-on result: मैंने तीनों Node.js examples local machine पर बिना external service के चलाए और basic queue flow, duplicate-delivery guard, और poison message का DLQ में जाना verify किया। Idempotency example prompt material के रूप में खास उपयोगी रहा, क्योंकि उसी email job की दूसरी delivery ने नया email भेजने के बजाय saved result reuse किया।

#Claude Code #job queue #async processing #BullMQ #Redis
मुफ़्त

मुफ़्त PDF: Claude Code cheatsheet

Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.

हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.

Masa

लेखक के बारे में

Masa

Claude Code workflow और team adoption पर काम करने वाला engineer.