Technology: Node.js Coding Task: Build a file upload system. Requirements: - Write clean and scalable code - Explain project archit…
Technology: Node.js
Coding Task: Create a task scheduler.
Requirements:
Bonus: Explain how you would deploy this project in production.
High-Level Task Scheduler in Node.js (Using Redis & BullMQ) To build a truly clean, scalable, and production-ready task scheduler in Node.js, relying solely on set timeout or in-memory cron packages (node-cron) is a fundamental anti-pattern because they don't scale across multiple server instances. The industry-standard architecture utilizes a Message Broker / Redis-backed Queue (like Bull MQ ) This ensures persistence, distributed processing, and graceful failure handling. 1. Project Architecture Overview Producer: Accepts the task (via API) and pushes it to the Redis queue with metadata (delay, priority, retries). Redis (Data Store): Acts as the in-memory persistence layer holding the scheduled and active jobs. Worker / Consumer: Listens to the Redis queue, processes jobs based on concurrency limits, and handles execution logic. 2. The Code Implementation const { Queue, Worker } = require('bullmq'); const IORedis = require('ioredis'); require('dotenv').config();
// Initialize Redis Connection const redisConnection = new IORedis(process.env.REDIS_URL, { maxRetriesPerRequest: null, enableReadyCheck: false });
// Create the Task Queue const taskQueue = new Queue('enterprise-scheduler', { connection: redisConnection });
/**
PRODUCER: Function to schedule tasks securely */ async function scheduleTask(jobName, payload, delayInMs = 0) { try { // Input Validation (Security Best Practice) if (!payload || typeof payload !== 'object') { throw new Error("Invalid payload structure."); }
const job = await taskQueue.add(jobName, payload, {
delay: delayInMs,
attempts: 3, // Edge case: Automatic retries
backoff: { type: 'exponential', delay: 2000 }, // Exponential backoff for rate limits
removeOnComplete: true, // Performance: Clear memory
removeOnFail: false // Keep failed jobs for auditing
});
console.log(`[Scheduled] Job ${job.id} will run after ${delayInMs}ms`);
return job.id;
} catch (error) {
console.error([Scheduling Error]: ${error.message});
}
}
/**
WORKER / CONSUMER: Processing tasks with concurrency limits
*/
const worker = new Worker('enterprise-scheduler', async job => {
console.log([Processing] Job ${job.id} | Type: ${job.name});
// Simulate heavy task execution (e.g., sending emails, DB updates) return new Promise((resolve, reject) => { setTimeout(() => { if (Math.random() < 0.1) return reject(new Error("Simulated API failure")); resolve({ success: true, timestamp: Date.now() }); }, 1000); }); }, { connection: redisConnection, concurrency: 10, // Performance: Process 10 jobs concurrently per instance limiter: { max: 100, // Security/Rate Limiting: Max 100 jobs duration: 1000 // per 1 second to avoid overwhelming third-party APIs } });
// EDGE CASES: Event Listeners for graceful handling
worker.on('failed', (job, err) => {
console.error([Failed] Job ${job.id} failed after retries: ${err.message});
// Alerting logic goes here (e.g., Send to Sentry/Slack)
});
worker.on('stalled', (jobId) => {
console.warn([Stalled] Job ${jobId} stalled. Event loop might be blocked.);
});
// Example Usage scheduleTask('send-welcome-email', { userId: '12345' }, 5000);
Duplicate Processing: By leveraging Redis, BullMQ ensures atomic locks, guaranteeing that a scheduled task is executed exactly once, even if multiple Node.js instances are running. 4. Performance Optimization Concurrency Control: The worker is set to concurrency: 10, preventing a massive influx of scheduled tasks from choking the Node.js single-threaded event loop. Memory Management: Implemented removeOnComplete: true to prevent Redis memory bloat from successful historical jobs. 5. Security Best Practices Rate Limiting: Added a built-in rate limiter (max: 100 per 1000ms) inside the worker to prevent DDoS-ing downstream services or third-party APIs. Input Sanitization: The producer strictly validates the incoming payload before allowing it into the Redis store, preventing injection attacks. Credential Masking: Redis connection strings and API keys are strictly maintained in a .env file, outside the codebase. 6. Bonus: Production Deployment Strategy To deploy this architecture securely and optimally in production: Containerization: Dockerize the Node.js application and the Redis instance. Use a docker-compose.yml for local staging and Kubernetes (K8s) for production scaling. Process Management: Instead of running just node app.js, deploy using PM2 in cluster mode (pm2 start worker.js -i max). This spins up multiple worker processes utilizing all CPU cores. Managed Redis: Do not run a local Redis container in production. Use a managed service like AWS ElastiCache or Redis Enterprise for built-in high availability, backups, and failover. Monitoring: Deploy bull-board (a UI dashboard for BullMQ) on a separate, strictly authenticated admin route to visually monitor queue health, retry failed jobs manually, and track latency metrics.
Sign in to post an answer
Technology: Node.js Coding Task: Implement JWT authentication system. Requirements: - Write clean and scalable code - Explain proje…
Technology: Node.js Coding Task: Build a file upload system. Requirements: - Write clean and scalable code - Explain project archit…
Technology: Node.js Coding Task: Create a pagination API. Requirements: - Write clean and scalable code - Explain project architect…
Technology: Node.js Coding Task: Implement rate limiting middleware. Requirements: - Write clean and scalable code - Explain projec…