# Pipelining

> Send many commands without waiting for each reply, from redis-cli or any HotKV SDK, to cut round trips.

URL: https://docs.hotkv.com/develop/pipelining/


Every command is a request and a reply. When a client waits for each reply
before sending the next command, most of the time goes to the network round
trip rather than to HotKV doing the work. Pipelining sends a batch of commands
back to back and reads all the replies afterwards, so a batch of a hundred
commands costs roughly one round trip instead of a hundred.

Pipelining is a client-side technique: the server reads commands from a
connection in order and replies in the same order, whether they arrive one at
a time or many at once. There is nothing to turn on.

## When to use it

- Loading or updating many keys at once (imports, cache warm-up, backfills).
- Reading a known set of keys, when one multi-key command such as
  [MGET](https://docs.hotkv.com/commands/mget/index.md) does not fit.
- Any loop that sends commands whose arguments do not depend on the previous
  reply.

A pipeline is not a transaction. Other clients' commands can run between the
commands of your batch, and if one command fails the others still run. For
all-or-nothing execution, wrap the batch in [MULTI and EXEC](https://docs.hotkv.com/develop/transactions/index.md)
or use a [Lua script](https://docs.hotkv.com/develop/lua-scripting/index.md).

## Pipelining with redis-cli

`redis-cli --pipe` reads commands from standard input, sends them without
waiting, and reports how many replies it got. It is the quickest way to bulk
load a file of commands:

```bash
cat > data.txt <<'EOF'
SET user:1 "Ada"
SET user:2 "Grace"
INCR stats:imports
EOF

cat data.txt | redis-cli --pipe
```

```
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 3
```

For large imports, generate the file in the RESP wire format instead of inline
commands, which handles values containing spaces, quotes or binary data. Each
command is an array of bulk strings:

```
*3\r\n$3\r\nSET\r\n$6\r\nuser:1\r\n$3\r\nAda\r\n
```

## Pipelining with the SDKs

Each HotKV SDK queues commands on a pipeline object and sends them in one
write when you execute it. The replies come back in the order you queued the
commands.

**Python**

```python
import hotkv

client = hotkv.HotKV(host="localhost", port=6379)

with client.pipeline(transaction=False) as pipe:
    pipe.set("user:1", "Ada")
    pipe.set("user:2", "Grace")
    pipe.incr("stats:imports")
    pipe.get("user:1")
    results = pipe.execute()

print(results)  # [True, True, 1, b'Ada']
```

`client.pipeline()` without arguments wraps the batch in `MULTI`/`EXEC`;
pass `transaction=False` for a plain pipeline. `execute(raise_on_error=False)`
returns a failed command's error in place of its result instead of raising.
**Node.js**

```ts
import { HotKV } from "@hotkv/client";

const client = new HotKV({ url: "hotkv://127.0.0.1:6379" });

const pipe = client.pipeline();
pipe.set("user:1", "Ada");
pipe.set("user:2", "Grace");
pipe.incr("stats:imports");
const name = pipe.get("user:1"); // settles once exec() runs

const results = await pipe.exec(); // [error, value] per command, in order
console.log(await name); // "Ada"
```

A failed command never aborts the rest of the batch: its entry in the
`exec()` result carries the error.
**Go**

```go
pipe := client.Pipeline()
pipe.Set(ctx, "user:1", "Ada")
pipe.Set(ctx, "user:2", "Grace")
pipe.Incr(ctx, "stats:imports")
name := pipe.Get(ctx, "user:1")

if _, err := pipe.Exec(ctx); err != nil {
    log.Fatal(err)
}
value, _ := name.Result()
fmt.Println(value) // "Ada"
```

The Go pipeline has typed methods for common commands and `pipe.Do(ctx, ...)`
for any other command.
**Java**

```java
import com.hotkv.Pipeline;
import com.hotkv.Response;

Pipeline pipeline = client.pipeline();
pipeline.set("user:1", "Ada");
pipeline.set("user:2", "Grace");
Response<Long> imports = pipeline.incr("stats:imports");
Response<String> name = pipeline.get("user:1");
pipeline.sync();

System.out.println(name.get() + " " + imports.get());
```

A failed command does not fail the batch: its `Response.get()` throws the
error for that command only.

## Batch size

Very large batches make the client hold every reply in memory until it reads
them, and make the server buffer replies for a client that is not reading yet.
Batches of a few hundred to a few thousand commands get almost all of the
benefit. For bulk loads, send several batches one after another rather than
one huge one.

> **Tip**
> Multi-key commands such as [MSET](https://docs.hotkv.com/commands/mset/index.md), [MGET](https://docs.hotkv.com/commands/mget/index.md),
> [HSET](https://docs.hotkv.com/commands/hset/index.md) with several fields, or [DEL](https://docs.hotkv.com/commands/del/index.md) with
> several keys do the same job in a single command when they fit your access
> pattern.

## Pipelining in a cluster

In a cluster, each command must go to the node that serves its key's slot.
The SDKs' cluster clients and `redis-cli -c` route single commands for you; if
you build pipelines by hand, group the commands by node, or give related keys
the same hash tag so they share a slot. See [Clustering](https://docs.hotkv.com/clustering/index.md).