Skip to content

Add DENYOOM flag to SUBSCRIBE, PSUBSCRIBE, SSUBSCRIBE, WATCH#4230

Open
enjoy-binbin wants to merge 1 commit into
valkey-io:unstablefrom
enjoy-binbin:deny_oom
Open

Add DENYOOM flag to SUBSCRIBE, PSUBSCRIBE, SSUBSCRIBE, WATCH#4230
enjoy-binbin wants to merge 1 commit into
valkey-io:unstablefrom
enjoy-binbin:deny_oom

Conversation

@enjoy-binbin

Copy link
Copy Markdown
Member

These commands previously lacked the DENYOOM flag, letting clients
bypass the maxmemory limit and exhaust server memory.

Follow-up to #3362, which made the memory used by watched keys and
pubsub channel/pattern names count toward each client's overhead so
the maxmemory-clients eviction can reclaim it. However, that only
helps when maxmemory-clients is configured.

This change marks them as DENYOOM so that the server will reject
these commands with an OOM error instead of accepting new subs and
watches that consume additional memory. Closing the gap where users
could otherwise bypass maxmemory by subscribing/watching.

Part of #3365 and closes #3365.

These commands previously lacked the DENYOOM flag, letting clients
bypass the maxmemory limit and exhaust server memory.

Follow-up to valkey-io#3362, which made the memory used by watched keys and
pubsub channel/pattern names count toward each client's overhead so
the maxmemory-clients eviction can reclaim it. However, that only
helps when maxmemory-clients is configured.

This change marks them as DENYOOM so that the server will reject
these commands with an OOM error instead of accepting new subs and
watches that consume additional memory. Closing the gap where users
could otherwise bypass maxmemory by subscribing/watching.

Part of valkey-io#3365.

Signed-off-by: Binbin <binloveplay1314@qq.com>
@enjoy-binbin enjoy-binbin added the major-decision-pending Major decision pending by TSC team label Jul 21, 2026
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The command definitions for SUBSCRIBE, PSUBSCRIBE, SSUBSCRIBE, and WATCH now include DENYOOM. Maxmemory tests verify rejection, successful execution with sufficient memory, and unaffected unsubscribe and publish commands.

Changes

DENYOOM command coverage

Layer / File(s) Summary
Mark commands with DENYOOM
src/commands/*.json, src/commands.def
Adds the DENYOOM flag to subscription and WATCH command definitions and refreshes the corresponding command table entries.
Validate OOM command handling
tests/unit/maxmemory.tcl
Tests rejection under noeviction and insufficient-eviction conditions, successful execution with sufficient memory, and unaffected unsubscribe and publish commands.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding DENYOOM to the affected commands.
Description check ✅ Passed The description matches the change set and explains the memory-protection motivation.
Linked Issues check ✅ Passed The PR adds DENYOOM to WATCH and SUBSCRIBE as required, and the extra subscribe variants stay aligned with the same bug fix.
Out of Scope Changes check ✅ Passed No clearly unrelated code changes are shown; the extra command/test updates support the same DENYOOM fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (2)
tests/unit/maxmemory.tcl (2)

663-667: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Replace potential missing helper procedures with standard client methods.

If the subscribe, psubscribe, and ssubscribe helper procedures are not globally available in the test suite (they are typically confined to tests/unit/pubsub.tcl), this test will fail with an invalid command name error.

Consider using the standard deferring client method calls ($rd command ... followed by $rd read) to ensure the test is self-contained and avoids dependencies on external unit test files. This also allows you to assert against the exact RESP array returned by the server.

♻️ Proposed refactor
-        set rd [valkey_deferring_client]
-        assert_equal {1} [subscribe $rd test_channel]
-        assert_equal {2} [psubscribe $rd test_pattern*]
-        assert_equal {1} [ssubscribe $rd test_shardchannel]
-        $rd close
+        set rd [valkey_deferring_client]
+        
+        $rd subscribe test_channel
+        assert_equal {subscribe test_channel 1} [$rd read]
+        
+        $rd psubscribe test_pattern*
+        assert_equal {psubscribe test_pattern* 1} [$rd read]
+        
+        $rd ssubscribe test_shardchannel
+        assert_equal {ssubscribe test_shardchannel 1} [$rd read]
+        
+        $rd close
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/maxmemory.tcl` around lines 663 - 667, Replace the subscribe,
psubscribe, and ssubscribe helper calls in this test with standard
deferring-client command and read operations on $rd. Assert the exact RESP
arrays returned for each subscription, then close $rd as before, keeping the
test independent of globally defined helper procedures.

663-667: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Replace potential missing helper procedures with standard client methods.

If the subscribe, psubscribe, and ssubscribe helper procedures are not globally available in the test suite (they are typically confined to tests/unit/pubsub.tcl), this test will fail with an invalid command name error.

Consider using the standard deferring client method calls ($rd command ... followed by $rd read) to ensure the test is self-contained and avoids dependencies on external unit test files. This also allows you to assert against the exact RESP array returned by the server.

♻️ Proposed refactor
-        set rd [valkey_deferring_client]
-        assert_equal {1} [subscribe $rd test_channel]
-        assert_equal {2} [psubscribe $rd test_pattern*]
-        assert_equal {1} [ssubscribe $rd test_shardchannel]
-        $rd close
+        set rd [valkey_deferring_client]
+        
+        $rd subscribe test_channel
+        assert_equal {subscribe test_channel 1} [$rd read]
+        
+        $rd psubscribe test_pattern*
+        assert_equal {psubscribe test_pattern* 2} [$rd read]
+        
+        $rd ssubscribe test_shardchannel
+        assert_equal {ssubscribe test_shardchannel 1} [$rd read]
+        
+        $rd close
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/maxmemory.tcl` around lines 663 - 667, Replace the subscribe,
psubscribe, and ssubscribe helper calls in this deferring-client setup with
standard `$rd command ...` requests followed by `$rd read` assertions. Keep the
existing channel and pattern arguments, and assert the exact RESP arrays
returned for each subscription before closing `$rd`, removing reliance on
globally defined test helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/unit/maxmemory.tcl`:
- Around line 663-667: Replace the subscribe, psubscribe, and ssubscribe helper
calls in this test with standard deferring-client command and read operations on
$rd. Assert the exact RESP arrays returned for each subscription, then close $rd
as before, keeping the test independent of globally defined helper procedures.
- Around line 663-667: Replace the subscribe, psubscribe, and ssubscribe helper
calls in this deferring-client setup with standard `$rd command ...` requests
followed by `$rd read` assertions. Keep the existing channel and pattern
arguments, and assert the exact RESP arrays returned for each subscription
before closing `$rd`, removing reliance on globally defined test helpers.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4ea99de2-6ef5-4e2d-9367-6cb78bababf6

📥 Commits

Reviewing files that changed from the base of the PR and between 0385d2f and 56b4c36.

📒 Files selected for processing (6)
  • src/commands.def
  • src/commands/psubscribe.json
  • src/commands/ssubscribe.json
  • src/commands/subscribe.json
  • src/commands/watch.json
  • tests/unit/maxmemory.tcl

@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.83%. Comparing base (50a0397) to head (56b4c36).
⚠️ Report is 1 commits behind head on unstable.

Additional details and impacted files
@@             Coverage Diff              @@
##           unstable    #4230      +/-   ##
============================================
+ Coverage     76.69%   76.83%   +0.14%     
============================================
  Files           162      162              
  Lines         81461    81477      +16     
============================================
+ Hits          62474    62606     +132     
+ Misses        18987    18871     -116     
Files with missing lines Coverage Δ
src/commands.def 100.00% <ø> (ø)

... and 25 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@valkey-review-bot valkey-review-bot 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.

I found one regression: marking SUBSCRIBE deny-oom also blocks Sentinel's internal hello-channel subscription when a monitored instance is already over maxmemory.

"function": "subscribeCommand",
"command_flags": [
"PUBSUB",
"DENYOOM",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUBSCRIBE is also the command Sentinel uses for its internal __sentinel__:hello pubsub link (src/sentinel.c:2411-2413). With DENYOOM here, an instance that is already over maxmemory rejects that subscription in processCommand() (src/server.c:4550-4570), so Sentinel never actually joins the hello channel. The callback on that link only handles pubsub message frames and ignores the error reply (src/sentinel.c:2879-2900), so the connection sits unsubscribed until the inactivity timeout closes and retries it (src/sentinel.c:4472-4474).

This needs an exemption for Sentinel’s internal hello subscription instead of making SUBSCRIBE deny-oom unconditionally.

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

Labels

major-decision-pending Major decision pending by TSC team

Projects

Status: Todo

Development

Successfully merging this pull request may close these issues.

[BUG] WATCH can consume large amounts of memory and bypasses OOM

1 participant