Skip to content

[Objects] HCOLLECT, ZCOLLECT: Do not delete keys that still hold unexpired fields or members - #2200

Merged
Badrish Chandramouli (badrishc) merged 2 commits into
microsoft:mainfrom
Laurianti:fix-collect-output-per-key
Oct 2, 2026
Merged

Badrish Chandramouli (badrishc) merged 2 commits into
microsoft:mainfrom
Laurianti:fix-collect-output-per-key

Conversation

@Laurianti

Copy link
Copy Markdown
Contributor

Root Cause

HashCollect and SortedSetCollect (listed keys) and ObjectCollect (* and the background collection task) created one ObjectOutput and passed it to the RMW of every key. Operate sets RemoveKey when a key becomes empty, and nothing cleared it, so every following key was deleted by InPlaceUpdater or PostCopyUpdater even with unexpired fields or members left. A WrongType flag carried over the same way, and an emptied key after a key of another type was left in place.

Description of Change

libs/server/Storage/Session/ObjectStore/HashOps.cs, SortedSetOps.cs and Common.cs: each key in the collection loops gets a new ObjectOutput, as the add-garnet-command skill describes for each call to the storage API. A key is now deleted only when its own collection empties it.

Tests

  • RespHashTests.HashCollectKeepsLiveFieldsOfEveryKey: hashes emptied by the collection are deleted, and hashes with an unexpired field keep it, with listed keys and with *.
  • RespHashTests.HashCollectRemovesEmptiedKeyAfterWrongTypeKey: HCOLLECT with a string key first replies WRONGTYPE and still deletes the emptied hash.
  • RespSortedSetTests.SortedSetCollectKeepsLiveMembersOfEveryKey: the same as the first test for ZCOLLECT.
  • The three tests fail without the change; restoring the shared output in any one of the three loops fails at least one of them. On main with this change, net10.0: Garnet.test.collections 785 passed, Garnet.test 1291 passed (9 skipped).

Issues Fixed

Fixes #2199

Copilot AI balanced review requested due to automatic review settings October 2, 2026 04:31

Copilot AI 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.

Copilot review overview

🟢 Approval recommended

The implementation directly fixes the shared-state defect and includes focused regression coverage for all affected paths.

Review effort: Balanced
Findings: None

What changed in this PR

Fixes cross-key state leakage in hash and sorted-set expiration collection.

Changes:

  • Creates a fresh ObjectOutput per collected key.
  • Prevents incorrect deletion and WrongType carryover.
  • Adds regression coverage for explicit and wildcard collection.
File Description
libs/​server/​Storage/​Session/​ObjectStore/​Common.cs Isolates output state during wildcard/background collection.
libs/​server/​Storage/​Session/​ObjectStore/​HashOps.cs Isolates each HCOLLECT key operation.
libs/​server/​Storage/​Session/​ObjectStore/​SortedSetOps.cs Isolates each ZCOLLECT key operation.
test/​standalone/​Garnet.test.collections/​RespHashTests.cs Tests live-field preservation and wrong-type ordering.
test/​standalone/​Garnet.test.collections/​RespSortedSetTests.cs Tests live-member preservation.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@badrishc
Badrish Chandramouli (badrishc) merged commit 143adec into microsoft:main Oct 2, 2026
5 checks passed
x@01 (x-at-01) added a commit to webc-fork/garnet that referenced this pull request Oct 4, 2026
…n missing key (microsoft#2192), ZADD XX INCR null (microsoft#2197), no empty object from object RMW (microsoft#2194), fresh ObjectOutput per key HCOLLECT/ZCOLLECT (microsoft#2200), CompletionEvent disposal + LogSizeTracker coalesce (microsoft#2198), dynamic test ports (microsoft#2193)
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.

HCOLLECT and ZCOLLECT delete keys that still hold unexpired fields or members

3 participants