redis/node-redis

Has the pipeline function been removed from node-redis?

itsvinayak opened this issue · 7 comments

Description

Hi,

I remember using the pipeline function in node-redis to batch commands and execute them together. Is this function now deprecated? Has it been replaced with automatic pipelining within the client?

Here's an example of how I've been using the pipeline function:

const redis = require("redis");
const client = redis.createClient();

client.on("error", (err) => {
  console.error("Error:", err);
});

function usePipelining() {
  // Create a pipeline within the client
  const pipeline = client.pipeline();

  pipeline.set("key1", "value1");
  pipeline.set("key2", "value2");
  pipeline.set("key3", "value3");
  pipeline.get("key1");
  pipeline.get("key2");
  pipeline.get("key3");

  // Execute the pipeline and handle results
  pipeline.exec((err, results) => {
    if (err) {
      if (err instanceof redis.RedisError) {
        console.error("Redis Error:", err);
      } else {
        console.error("Pipeline Execution Error:", err);
      }
      return;
    }

    console.log("Pipeline results:");
    results.forEach((result, index) => {
      if (result === null) {
        console.log(`Key ${index + 1} doesn't exist`);
      } else {
        console.log(result);
      }
    });
    client.quit();
  });
}

usePipelining();

Can someone clarify if the pipeline function is still the recommended way to batch commands, or should we rely on automatic pipelining features in newer versions of node-redis?

Environment Details:

Node.js version: v22.2.0
node-redis version: v4.6.15
Redis Server Version: v7.0.15
Operating System: Ubuntu 24.04

commands that are written on the same "tick" are auto pipelined, so you can just:

const replies = await Promise.all([
  client.get('a'),
  client.get('b')
]);

you can also execute a multi as a pipeline (without the "MULTI" and "EXEC" wrappers):

const replies = await client.multi()
  .get('a')
  .get('b')
  .execAsPipeline();

Hi @leibale

Has client.pipeline() or client.batch() been deprecated?

For documentation purposes

execAsPipeline:

  • Purpose: Similar to exec, this command executes the queued commands as a pipeline instead of a transaction.
  • Non-atomicity: execAsPipeline doesn't guarantee atomicity. It sends commands to the server in a single network round trip but doesn't ensure that the operations are executed as a single atomic block.
  • Performance: It can be faster for bulk operations since it reduces the number of network round trips but lacks atomic guarantees.

@leibale please correct me if I'm wrong.

@itsvinayak sorry for the late reply, but:
"Similar to exec, this command executes the queued commands as a pipeline instead of a transaction." - correct.
"Non-atomicity: execAsPipeline doesn't guarantee atomicity. It sends commands to the server in a single network round trip but doesn't ensure that the operations are executed as a single atomic block." - not 100% correct, "It sends commands to the server in a single network round trip" - it'll be executed in "one round trip" as long as the socket "can handle it" (it will follow the "drain" events).
"Performance: It can be faster for bulk operations since it reduces the number of network round trips but lacks atomic guarantees." - with auto pipelining on the performance of these 2 should be similar:

await client.multi()
  .get('a')
  .get('b')
  .execAsPipeline();

await Promise.all([
  client.get('a'),
  client.get('b')
]);

TBH, the only reason to use "pipeline" is:

  1. when sometimes you execute it as a multi
const mutli = client.multi()
  .ping();

if (Math.random() > 0.5) {
  await multi
    .set('a', 'b')
    .exec();
} else {
  await multi.execAsPipeline();
}
  1. when you are reusing the same "pipeline" multiple times
const incrPipeline = client.multi()
  .incr('a')
  .incr('b');

await Promise.all([
  incrPipeline.execAsPipeline(),
  incrPipeline.execAsPipeline()
]);

Thank you for providing clarification, @leibale