Skip to content

(feat) Adds client support for the XNACK command#3790

Open
elena-kolevska wants to merge 3 commits intoredis:masterfrom
elena-kolevska:feat/8.8-xnack
Open

(feat) Adds client support for the XNACK command#3790
elena-kolevska wants to merge 3 commits intoredis:masterfrom
elena-kolevska:feat/8.8-xnack

Conversation

@elena-kolevska
Copy link
Copy Markdown
Contributor

@elena-kolevska elena-kolevska commented Apr 23, 2026

Adds client support for the XNACK command introduced in Redis 8.8 (redis/redis#14797).

XNACK negatively acknowledges one or more messages in a consumer group's Pending Entries List (PEL), releasing them back to the group for redelivery without removing them from the PEL. This gives consumers finer-grained control over failure semantics compared to simply leaving a message unacknowledged.

client.XNack(ctx, &redis.XNackArgs{
    Stream: "my-stream",
    Group:  "my-group",
    Mode:   "FAIL",         // SILENT | FAIL | FATAL
    IDs:    []string{"1-0", "2-0"},
})

Note

Low Risk
Mostly additive stream-command plumbing plus new tests/doctest updates; low risk aside from potential argument formatting/Redis version gating issues.

Overview
Adds Redis 8.8+ support for the XNACK consumer-group command via a new XNackArgs struct and Client.XNack method, and exposes it on StreamCmdable.

Extends the stream command test suite to cover required Mode, the SILENT/FAIL/FATAL behaviors (including RetryCount, Force, and nacked-count visibility), and updates the stream tutorial doctest with an XNack example and renumbered variables.

Reviewed by Cursor Bugbot for commit 26b1e89. Bugbot is set up for automated code reviews on this repo. Configure here.

@elena-kolevska elena-kolevska changed the title Feat/8.8 xnack Adds client support for the XNACK command Apr 28, 2026
@elena-kolevska elena-kolevska changed the title Adds client support for the XNACK command (feat) Adds client support for the XNACK command Apr 28, 2026
@elena-kolevska elena-kolevska marked this pull request as ready for review April 28, 2026 22:41
Copy link
Copy Markdown
Collaborator

@ofekshenawa ofekshenawa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Copy Markdown
Member

@ndyakov ndyakov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left no blocking suggestions that we can discuss before merging. Let's align with the rest of the clients.

fmt.Println(res31a) // >>> 0-0
// STEP_END

// STEP_START xnack
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should contact the docs team to include this example

Comment thread stream_commands.go
// - "FATAL": the message is invalid or suspected malicious. The delivery
// counter is set to MAXINT, which will immediately move the message to
// the Dead Letter Queue (DLQ) if one is configured for the group.
Mode string
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we define this as iota or just have the accepted values as constants?

Comment thread stream_commands.go
// RetryCount sets the delivery counter to an explicit value, overriding the
// counter adjustment that would otherwise be applied by Mode.
// Leave nil to let Mode control the counter (the common case).
RetryCount *uint64
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although I am not keen to have this as a pointer maybe it is the only logical solution to distinguish between empty value and zero.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants