Skip to content

Fix crash when active field-expiry leaves a single-entry HT vset buck…#3957

Merged
ranshid merged 1 commit into
valkey-io:9.1from
ranshid:backport-9.1
Jun 10, 2026
Merged

Fix crash when active field-expiry leaves a single-entry HT vset buck…#3957
ranshid merged 1 commit into
valkey-io:9.1from
ranshid:backport-9.1

Conversation

@ranshid

@ranshid ranshid commented Jun 10, 2026

Copy link
Copy Markdown
Member

backport of (#3950)

When a hash has many fields whose expirations fall in the same volatile-set time-bucket (>127 entries), that bucket is encoded as a hashtable (HT). The active field-expiry path drains expired entries via vsetBucketRemoveExpired_HASHTABLE(), but it is bounded by the per-key expire quota (max_count, from dbReclaimExpiredFields). When the quota stops the drain one entry short, the function only collapsed the bucket to NONE at size 0 and left it as an HT bucket holding a single entry.

That violates the invariant enforced by removeFromBucket_HASHTABLE(), which asserts hashtableSize(ht) > 0 after a delete and downgrades an HT bucket to SINGLE at size 1. A later normal removal of that lone field -- HDEL, or a cross-bucket HSETEX update routed through removeEntryFromRaxBucket() -- deletes the sole entry, drops the size 1 -> 0, and trips the assertion.

Fix: Apply the same downgrade in the expiry path: when draining leaves exactly one entry, downgrade the HT bucket to SINGLE so the invariant holds. Behavior is unchanged when the bucket drains fully (-> NONE) or retains two or more entries (-> HT, as the normal removal path already tolerates).

Add a regression test that fills one time-bucket past the HT threshold, expires all but one entry, then removes the survivor through the normal path; it crashed before this fix and passes after.

…et (valkey-io#3950)

When a hash has many fields whose expirations fall in the same
volatile-set time-bucket (>127 entries), that bucket is encoded as a
hashtable (HT). The active field-expiry path drains expired entries via
vsetBucketRemoveExpired_HASHTABLE(), but it is bounded by the per-key
expire quota (max_count, from dbReclaimExpiredFields). When the quota
stops the drain one entry short, the function only collapsed the bucket
to NONE at size 0 and left it as an HT bucket holding a single entry.

That violates the invariant enforced by removeFromBucket_HASHTABLE(),
which asserts hashtableSize(ht) > 0 after a delete and downgrades an HT
bucket to SINGLE at size 1. A later normal removal of that lone field --
HDEL, or a cross-bucket HSETEX update routed through
removeEntryFromRaxBucket() -- deletes the sole entry, drops the size 1
-> 0, and trips the assertion.

Fix: Apply the same downgrade in the expiry path: when draining leaves
exactly one entry, downgrade the HT bucket to SINGLE so the invariant
holds. Behavior is unchanged when the bucket drains fully (-> NONE) or
retains two or more entries (-> HT, as the normal removal path already
tolerates).

Add a regression test that fills one time-bucket past the HT threshold,
expires all but one entry, then removes the survivor through the normal
path; it crashed before this fix and passes after.

Signed-off-by: Ran Shidlansik <ranshid@amazon.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b63f4240-dff8-4ac7-8551-902a4288a21c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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 and usage tips.

@ranshid ranshid added release-notes This issue should get a line item in the release notes bug Something isn't working labels Jun 10, 2026
@ranshid ranshid requested a review from enjoy-binbin June 10, 2026 04:50
@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 76.36%. Comparing base (c9e8005) to head (b428b97).

Additional details and impacted files
@@            Coverage Diff             @@
##              9.1    #3957      +/-   ##
==========================================
- Coverage   76.64%   76.36%   -0.29%     
==========================================
  Files         163      163              
  Lines       81003    81027      +24     
==========================================
- Hits        62086    61875     -211     
- Misses      18917    19152     +235     
Files with missing lines Coverage Δ
src/unit/test_vset.cpp 99.32% <100.00%> (+0.04%) ⬆️
src/vset.c 88.08% <100.00%> (+0.09%) ⬆️

... and 29 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.

@enjoy-binbin enjoy-binbin left a comment

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.

OK, I thought we already had a backport bot.

@ranshid

ranshid commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

OK, I thought we already had a backport bot.

you are probably right. I thought we need to remove the unitests, but actually in 9.1 we can still have them

@ranshid ranshid merged commit 5789b3c into valkey-io:9.1 Jun 10, 2026
112 of 116 checks passed
@github-project-automation github-project-automation Bot moved this to To be backported in Valkey 9.1 Jun 10, 2026
@valkeyrie-ops valkeyrie-ops Bot moved this from To be backported to Done in Valkey 9.1 Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working release-notes This issue should get a line item in the release notes

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants