Redis Core Operations Implementation: On LeetCode Architecture

Redis is a versatile, open-source, in-memory data store often used for caching, databases, and queues. Here's how Redis can be implemented for core operations and as a queue system.


Core Redis Commands

SET/GET/DEL

Basic commands to store, retrieve, and delete data:

  • Set Data:

      SET mykey "Hello"
    
  • Get Data:

      GET mykey
    

    Delete Data:

      DEL mykey
    

HSET/HGET/HDEL (Hashes)

Hash commands store fields and values under a single key:

HSET user:100 name "John Doe" email "user@example.com" age "30"  
HGET user:100 name  
HGET user:100 email

Redis as a Queue

Redis excels as a lightweight queue system. For example, platforms like LeetCode handle asynchronous processing using queues.

Pushing to a Queue

Pushes items to the left (head) of the queue:

LPUSH problems 1  
LPUSH problems 2

Popping from a Queue

Pops items from the right (tail) of the queue:

RPOP problems

Blocked Pop (BRPOP)

Blocks the operation until data is available or the timeout is reached:

  • Indefinite Block:

      BRPOP problems 0
    
  • Timeout Block:

      BRPOP problems 30
    

Backend Server Implementation: LeetCode Queue Architecture

Below is an example of a Node.js server using Redis to queue data:

Backend Code

javascriptCopy codeimport express from "express";  
import { createClient } from "redis";  

const app = express();  
const client = createClient();  

client.on("error", (err) => console.log("Redis Client Error", err));  
client.connect();  

app.use(express.json());  

app.post("/submit", async (req, res) => {  
  const { name, email, message } = req.body;  
  try {  
    await client.lPush("messages", JSON.stringify({ name, email, message }));  
    res.json({ success: true, message: "Message sent successfully" });  
  } catch (error) {  
    res.json({ success: false, message: "Failed to send message" });  
  }  
});  

app.listen(3001, () => {  
  console.log("Server is running on port 3001");  
});

Worker Code

A worker continuously monitors the queue and processes tasks:

javascriptCopy codeimport { createClient } from "redis";  

const client = createClient();  

async function main() {  
  await client.connect().then(() => {  
    console.log("Connected to Redis");  
  }).catch((err) => {  
    console.error("Redis connection error:", err);  
  });  

  while (true) {  
    const result = await client.brPop("messages", 0);  
    // Simulate task execution (e.g., running user code in Docker)  
    console.log("Message received: ", result);  

    await new Promise((resolve) => setTimeout(resolve, 1000));  
  }  
}  
main();

How It Works

  • Primary Backend: Accepts user requests (e.g., via POST) and queues data in Redis.

    e.g., See how the message is queued so that it can be assigned to a worker.

  • Worker Nodes: Monitor the queue and process tasks in parallel. Redis handles load balancing among workers.

  • Fault Tolerance: If workers go down, Redis ensures queued data persists. Once workers recover, they resume processing seamlessly.

Redis provides scalability and resilience, making it ideal for asynchronous processing scenarios like LeetCode submissions or video transcoding.

Pub subs

Publish-subscribe (pub-sub) is a messaging pattern where messages are sent to a topic without knowing who, if anyone, will receive them, and subscribers listen for messages on topics they are interested in without knowing the source. This separation of publishers and subscribers enables highly scalable and flexible communication systems.

Subscribe to a topic

For example, to subscribe to a topic named problems_done, you would use:

SUBSCRIBE problems_done

Publishing to a topic

For example, to publish the message {id: 1, ans: 'TLE'} to the problems_done topic, you would use

PUBLISH problems_done "{id: 1, ans: 'TLE'}"

Code for Node.js Implementation of Pub/Sub

async function processSubmission(messages:string) {
  const {programId, code, language} = JSON.parse(messages);

  console.log(`Received message from ${programId} <${code}>: ${language}`);
  // here we can run the user code on docker container  

  await new Promise((resolve) => setTimeout(resolve, 1000));

  console.log("Message processed");
  client.publish("ProblemDone", JSON.stringify({ programId, code, language, status: "TLE" }));
}
processSubmission(result.element);

Output:

Full architecture of what we implemented here

Its versatility makes Redis an excellent choice for building fault-tolerant, high-performance systems. Whether you're processing LeetCode submissions or building scalable microservices, Redis is a tool worth mastering.