Course contentsShow
← Node.js
Lesson 1186 of 1,98010. Real-time Communication and WebsocketsFree lesson

Ensuring Idempotency in Event Processing

You will design event handlers that are safe to process multiple times in case of retries.

In this lesson, you will learn how to design WebSocket event handlers that safely handle duplicate messages, ensuring your system state remains consistent even when retries occur.

The Problem with Retries

In distributed real-time systems, network jitters or acknowledgment failures often cause clients to resend events. If your event handler simply performs an operation like database.increment('balance', 10), a single user action accidentally sent twice will result in an incorrect balance. This violates the principle of idempotency: the property that performing an operation multiple times has the same effect as performing it once.

Designing Idempotent Handlers

To make an event handler idempotent, you must move away from "relative" operations (like increment) and move toward "deterministic" state transitions, typically by using a unique event ID sent by the client.

When an event arrives, check if you have already processed its unique identifier. You can store these IDs in a high-speed cache like Redis with a Time-to-Live (TTL) that matches your deduplication window.

async function handlePayment(event, socket) {
  const { eventId, amount, userId } = event;

  // 1. Check if the event ID has been processed
  const alreadyProcessed = await redis.get(`event:${eventId}`);
  if (alreadyProcessed) {
    console.log(`Duplicate event ${eventId} ignored.`);
    return;
  }

  // 2. Perform the business logic
  await db.payments.create({ userId, amount, eventId });

  // 3. Mark as processed with an expiration
  await redis.set(`event:${eventId}`, 'true', 'EX', 3600);
  
  socket.emit('payment_confirmed', { eventId });
}

Why This Works

By requiring a unique eventId for every significant state change, you treat the event as a signal of intent rather than a command to perform a raw calculation. Even if the network hangs and the client sends the same payload five times, your database will only record the transaction once because the cache blocks the subsequent attempts. This pattern is essential for reliable messaging over unstable WebSocket connections where strict ordering and delivery guarantees are difficult to enforce.

Key Takeaway: Never assume a message is unique; always implement deduplication by validating a unique event identifier against a fast, transient storage layer before executing state-changing logic.