Skip to content

[Redis 8.10] Add SUNIONCARD and SDIFFCARD set cardinality commands - #3874

Merged
uglide merged 1 commit into
mainfrom
im/8_10-new-set-card-commands_2
Aug 7, 2026
Merged

[Redis 8.10] Add SUNIONCARD and SDIFFCARD set cardinality commands#3874
uglide merged 1 commit into
mainfrom
im/8_10-new-set-card-commands_2

Conversation

@uglide

@uglide uglide commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds support for the two set-cardinality commands shipping in Redis 8.10: SUNIONCARD numkeys key [key ...] [APPROX] [LIMIT limit] (server PR redis/redis#14893) and SDIFFCARD numkeys key [key ...] [LIMIT limit] (redis/redis#15278). They return the cardinality of a set union / difference without materializing or returning the derived set; LIMIT caps the result and allows server-side early termination (LIMIT 0 = no limit), and SUNIONCARD APPROX returns a HyperLogLog-based estimate with fixed, small server-memory overhead for large-set use cases.

All behavior was verified against a live 8.10 pre-release server before implementation:

SADD tags:sports football tennis golf      SADD tags:news politics football
SUNIONCARD 2 tags:sports tags:news             -> (integer) 4
SUNIONCARD 2 tags:sports tags:news LIMIT 3     -> (integer) 3   (capped; real = 4)
SUNIONCARD 2 tags:sports tags:news APPROX      -> (integer) 4

SADD visitors:mon alice bob carol dave eve     SADD visitors:tue carol dave frank
SDIFFCARD 2 visitors:mon visitors:tue          -> (integer) 3
SDIFFCARD 2 visitors:mon visitors:tue LIMIT 1  -> (integer) 1   (existence check)
SDIFFCARD 2 visitors:mon visitors:tue APPROX   -> ERR syntax error

Key Decisions & Assumptions

  • Options are dedicated CompositeArgument classes for both commands, passed as the last parameter: SUnionCardArgs (APPROX, LIMIT) and SDiffCardArgs (LIMIT). SDiffCardArgs keeps the diff API future-proof for when the server defines SDIFFCARD APPROX semantics (today it rejects it with a syntax error, so no approx() is exposed there).
  • Two-key and List<K> overloads instead of varargssunioncard(K, K), sunioncard(List<K>) and the same pair with a trailing args parameter, mirrored for sdiffcard — 8 methods per interface flavor. The two-key shape covers the dominant call pattern; the List overload replaces varargs so the options object can sit last without ambiguity and callers avoid generic-varargs array creation. No single-key overload is exposed — a one-key union/difference is just SCARD.
  • SUnionCardArgs always emits APPROX before LIMIT (the canonical order per the design doc), regardless of setter order; each option is emitted at most once.
  • Replies are plain integers under both RESP2 and RESP3 (verified live), so the existing IntegerOutput is reused and no RESP2-specific test overload is needed.
  • Both commands are READONLY and registered in ReadOnlyCommands for replica-read routing. In cluster mode they follow standard multi-key single-slot routing (like SINTER/SDIFF); no fan-out overrides — the command tips forbid splitting/aggregating across shards.
  • New commands are wired through all six interface flavors (sync, async, reactive, Kotlin coroutines, node-selection ×2) plus builder and both dispatch layers; the API consistency suite passes with no deviation entries.

Behavioral / Conceptual Changes

  • New public API on the set command interfaces, all @since 7.7, plus the new SUnionCardArgs/SDiffCardArgs types and the APPROX CommandKeyword.
  • LIMIT 0 is passed through as-is (server semantics: no limit); negative limits are rejected client-side with IllegalArgumentException.
  • Against servers older than 8.10 the commands fail with the server's ERR unknown command reply — no client-side version gating, consistent with other command additions.

Testing

  • SUnionCardArgsUnitTests / SDiffCardArgsUnitTests (new): encoded token order incl. APPROX-before-LIMIT normalization, LIMIT 0 passthrough, negative-limit rejection.
  • RedisCommandBuilderUnitTests: full wire-level assertions for the builder overloads (numkeys placement, key encoding, option order) plus empty-keys/null-args validation.
  • SetCommandIntegrationTests: new tests gated with @EnabledOnCommand("SUNIONCARD") / @EnabledOnCommand("SDIFFCARD") covering exact/APPROX/LIMIT/LIMIT 0 and missing first/subtrahend keys; they run unchanged through the existing reactive and transactional overload classes (all green against the 8.10 test env, none skipped).
  • ClusterReadOnlyCommandsUnitTests count bumped for the two new read-only registrations.

Notes

  • Design doc: "[8.10] SUNIONCARD, SDIFFCARD: Set Cardinality Commands — High Level Design Document".
  • The v8.10 test-image pin already contains both commands; no .env bump needed.
  • SINTERCARD is (pre-existing) missing from ReadOnlyCommands; left untouched as out of scope.

🤖 Generated with Claude Code


Note

Low Risk
Additive API and command encoding only; no changes to existing command behavior or security-sensitive paths.

Overview
Adds client support for Redis 8.10 SUNIONCARD and SDIFFCARD, which return union/difference cardinality without materializing the set. SUnionCardArgs supports APPROX and LIMIT; SDiffCardArgs supports LIMIT only (no approx() until the server allows it).

Each command is exposed as two-key and List<K> overloads, with optional trailing args objects (eight methods per API surface). Wiring follows existing set commands: RedisCommandBuilder, async/reactive dispatch, sync/async/reactive set interfaces, cluster node-selection APIs, and Kotlin coroutines. Protocol updates register SDIFFCARD / SUNIONCARD, add APPROX to CommandKeyword, and mark both as read-only for replica routing.

Client-side validation rejects empty key lists and negative LIMIT values; LIMIT 0 is sent through as “no limit.” Tests cover args encoding, RESP wire format, integration behind @EnabledOnCommand, and the cluster read-only command count bump.

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a16f0a9d53

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/main/java/io/lettuce/core/api/sync/RedisSetCommands.java Outdated
* @see SUnionCardArgs#limit(long)
* @since 7.7
*/
public static SUnionCardArgs limit(long limit) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Document validation on both limit factories

When callers pass a negative value to this factory, it delegates to limit(long) and throws IllegalArgumentException, but the factory Javadoc omits that contractual exception; SDiffCardArgs.Builder.limit(long) has the same omission. Add the matching @throws IllegalArgumentException if {@code limit} is negative. tag to both factory methods so callers see the validation at the entry point they invoke.

AGENTS.md reference: AGENTS.md:L153-L154

Useful? React with 👍 / 👎.

Comment thread src/main/java/io/lettuce/core/api/sync/RedisSetCommands.java Outdated
@uglide
uglide requested review from a-TODO-rov and ggivo July 30, 2026 16:09
@uglide
uglide force-pushed the im/8_10-new-set-card-commands_2 branch from a16f0a9 to 86f7096 Compare July 31, 2026 06:08

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 86f709608d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/main/java/io/lettuce/core/api/sync/RedisSetCommands.java

@ggivo ggivo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This introduces List for multi-key commands, while the rest of the API uniformly uses K... keys as the last parameter (sdiff, sintercard, mget, …). Is this an intentional new convention?

Comment thread src/main/java/io/lettuce/core/api/async/RedisSetAsyncCommands.java Outdated
Comment thread src/main/java/io/lettuce/core/api/sync/RedisSetCommands.java Outdated
@uglide
uglide force-pushed the im/8_10-new-set-card-commands_2 branch from 86f7096 to e856324 Compare August 3, 2026 12:17
@uglide
uglide force-pushed the im/replace-adding-command-skill-with-extend-skill branch 5 times, most recently from ae63108 to 20c3c66 Compare August 6, 2026 14:09
Base automatically changed from im/replace-adding-command-skill-with-extend-skill to main August 6, 2026 14:22
@uglide
uglide force-pushed the im/8_10-new-set-card-commands_2 branch from e856324 to deb8e80 Compare August 6, 2026 15:58
@uglide
uglide merged commit 0bc51c7 into main Aug 7, 2026
11 of 18 checks passed
@uglide
uglide deleted the im/8_10-new-set-card-commands_2 branch August 7, 2026 08:51
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.

2 participants