Skip to content

Replace fragile dict stored-key API with getKeyId callback#14

Open
tomerqodo wants to merge 2 commits intocoderabbit_combined_20260121_augment_sentry_coderabbit_1_base_replace_fragile_dict_stored-key_api_with_getkeyid_callback_pr90from
coderabbit_combined_20260121_augment_sentry_coderabbit_1_head_replace_fragile_dict_stored-key_api_with_getkeyid_callback_pr90
Open

Replace fragile dict stored-key API with getKeyId callback#14
tomerqodo wants to merge 2 commits intocoderabbit_combined_20260121_augment_sentry_coderabbit_1_base_replace_fragile_dict_stored-key_api_with_getkeyid_callback_pr90from
coderabbit_combined_20260121_augment_sentry_coderabbit_1_head_replace_fragile_dict_stored-key_api_with_getkeyid_callback_pr90

Conversation

@tomerqodo
Copy link

@tomerqodo tomerqodo commented Jan 22, 2026

Benchmark PR from qodo-benchmark#90

Summary by CodeRabbit

  • Refactor
    • Optimized internal dictionary and key storage handling mechanisms, simplifying API usage and improving efficiency.
    • Refined function signatures and internal hash-related operations for better consistency.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

Walkthrough

This PR refactors Redis's stored-key handling mechanism by introducing a keyFromStoredKey callback to extract actual keys from stored-key representations, replacing the previous dictUseStoredKeyApi toggle approach. Function signatures are updated to annotate stored-key parameters, and the isSdsField parameter is removed from hashTypeDelete.

Changes

Cohort / File(s) Summary
Dict core mechanism overhaul
src/dict.h, src/dict.c
Introduces __stored_key macro and keyFromStoredKey callback in dictType to replace storedHashFunction/storedKeyCompare. Adds dictStoredKey2Key() utility for key extraction. Updates 7 public function signatures (dictAdd, dictAddRaw, dictInsertKeyAtLink, dictReplace, dictAddOrFind, dictSetKeyAtLink, dictSetKey) to accept stored-key parameters. Converts pauseAutoResize from bit-field to int16_t and removes useStoredKeyApi field.
Stored-key API toggle removal
src/defrag.c, src/kvstore.c, src/rdb.c
Removes dictUseStoredKeyApi calls that previously toggled stored-key mode around dict operations. In defrag.c, replaces stored-key API toggling with direct dictStoredKey2Key() call. Removes corresponding toggle pairs in kvstore.c and rdb.c (3 occurrences).
Dict type implementations
src/server.c, src/server.h
Replaces storedHashFunction and storedKeyCompare with keyFromStoredKey in dbDictType and dbExpiresDictType. Converts dictHashKV() to kvGetKey() to return key instead of hash. Updates dictSdsCompareKV() signature for consistency.
Hash operation refactoring
src/t_hash.c, src/server.h
Removes isSdsField parameter from hashTypeDelete() signature (public declaration and implementation), affecting all call sites.
Supporting updates
src/module.c, src/memory_prefetch.c
In module's RM_HashSet, removes 3rd argument from hashTypeDelete() call. In prefetchValueData, introduces local key variable via kvobjGetKey() for dict key comparisons.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 The stored keys now flow through callbacks, so neat,
No more toggling dances—extraction's the beat!
With keyFromStoredKey, the dicts are refined,
Cleaner signatures bloom, old APIs left behind,
A refactor complete, let hashing proceed! 🔑✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.79% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main refactoring: replacing the fragile dictUseStoredKeyApi mechanism with a new keyFromStoredKey callback approach throughout the codebase.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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

@tomerqodo
Copy link
Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/defrag.c (1)

274-282: Fix build break and use the derived key for lookup.

CI is failing with an undefined reference to dictStoredKey2Key, and dictFindLink is still called with the stored-key pointer even though you now compute the real key. After removing dictUseStoredKeyApi, this lookup should use the derived key. Consider inlining the conversion (or export the helper) and pass the derived key to dictFindLink.

🛠️ Suggested fix (avoids the undefined reference and uses the key)
-    const void *key = dictStoredKey2Key(d, ptr);
-    link = dictFindLink(d, ptr, NULL);
+    const void *key = d->type->keyFromStoredKey ? d->type->keyFromStoredKey(ptr) : ptr;
+    link = dictFindLink(d, key, NULL);
🤖 Fix all issues with AI agents
In `@src/dict.c`:
- Around line 97-108: The function validateStoredKeyConversion is defined but
unused, causing -Werror=unused-function; either remove the function entirely or
invoke it in appropriate places (e.g., call validateStoredKeyConversion(d, key)
from dictAddRaw during debug/test builds or wrap its use under REDIS_TEST)
and/or replace it with an assertion that calls dictStoredKey2Key(d, key) to
validate behavior; update references to dictStoredKey2Key and ensure any added
call is only in non-production builds (DEBUG/REDIS_TEST) to avoid runtime
overhead.
🧹 Nitpick comments (1)
src/server.h (1)

3658-3658: Minor: Parameter name inconsistency between declaration and definition.

The declaration uses key but the implementation in src/t_hash.c uses field. For hash operations, field is more semantically accurate since this function deletes hash fields, not keys (which refer to the hash object itself in Redis terminology).

Suggested fix
-int hashTypeDelete(robj *o, void *key);
+int hashTypeDelete(robj *o, void *field);
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c7ec36d and 9c4a41c.

📒 Files selected for processing (10)
  • src/defrag.c
  • src/dict.c
  • src/dict.h
  • src/kvstore.c
  • src/memory_prefetch.c
  • src/module.c
  • src/rdb.c
  • src/server.c
  • src/server.h
  • src/t_hash.c
💤 Files with no reviewable changes (2)
  • src/rdb.c
  • src/kvstore.c
🧰 Additional context used
🧬 Code graph analysis (5)
src/server.h (1)
src/t_hash.c (1)
  • hashTypeDelete (1257-1297)
src/module.c (1)
src/t_hash.c (1)
  • hashTypeDelete (1257-1297)
src/memory_prefetch.c (2)
src/object.c (1)
  • kvobjGetKey (234-246)
src/dict.c (2)
  • dictGetNext (1081-1085)
  • dictCompareKeys (502-506)
src/defrag.c (1)
src/dict.c (1)
  • dictStoredKey2Key (97-99)
src/server.c (2)
src/object.c (1)
  • kvobjGetKey (234-246)
src/sds.h (1)
  • sdslen (98-111)
🪛 Cppcheck (2.19.0)
src/dict.c

[error] 341-341: failed to evaluate #if condition, undefined function-like macro invocation

(syntaxError)

🪛 GitHub Actions: CI
src/dict.c

[error] 102-102: Build failed due to -Werror: unused function 'validateStoredKeyConversion' in src/dict.c. Command: make REDIS_CFLAGS='-Werror' BUILD_TLS=yes.

🪛 GitHub Actions: Codecov
src/t_hash.c

[error] 1-1: Command failed: make -C src lcov (lcov step failed due to the above link error).

src/server.h

[error] 1-1: Command failed: make -C src lcov (lcov step failed due to the above link error).

src/module.c

[error] 1-1: Command failed: make -C src lcov (lcov step failed due to the above link error).

src/memory_prefetch.c

[error] 1-1: Command failed: make -C src lcov (lcov step failed due to the above link error).

src/defrag.c

[error] 280-280: Linking failed: undefined reference to 'dictStoredKey2Key' in activeDefragHfieldAndUpdateRef. This function may be missing implementation or prototype/configuration in the dict subsystem.


[error] 1-1: Command failed: make -C src lcov (lcov step failed due to the above link error).

src/dict.h

[error] 1-1: Command failed: make -C src lcov (lcov step failed due to the above link error).

src/dict.c

[error] 1-1: Command failed: make -C src lcov (lcov step failed due to the above link error).

src/server.c

[error] 1-1: Command failed: make -C src lcov (lcov step failed due to the above link error).

🪛 GitHub Actions: CodeQL
src/defrag.c

[error] 280-280: Undefined reference to 'dictStoredKey2Key' during linking, indicating missing implementation or missing object file for this function.

🪛 GitHub Actions: External Server Tests
src/dict.c

[error] 102-102: validateStoredKeyConversion defined but not used. (-Werror=unused-function)

🔇 Additional comments (27)
src/t_hash.c (7)

1256-1259: Function signature simplification looks correct.

The removal of the isSdsField parameter is consistent with the PR objective. All call sites in this file pass SDS strings, and the implementation correctly casts the void *field to sds at line 1259.

The comment accurately documents the expected type. Consider whether the parameter type could be changed from void *field to sds field to make the contract explicit at the type level, though the current approach may be intentional for API consistency.


537-537: LGTM!

Call site correctly updated to the new two-parameter signature.


764-764: LGTM!

Call site correctly updated with assertion on the return value.


1112-1112: LGTM!

Call site correctly updated to the new signature.


2723-2723: LGTM!

Call site correctly updated; c->argv[i]->ptr is an SDS for string objects.


2928-2928: LGTM!

Call site correctly updated to the new two-parameter signature.


3503-3503: LGTM!

Call site correctly updated; field is obtained from entryGetField(e) which returns SDS.

src/memory_prefetch.c (1)

233-238: The code change correctly extracts the key before comparison. While kvobjGetKey() technically can return NULL when kv->iskvobj is false, all 30+ uses of this function throughout the codebase proceed without NULL checks, indicating this is a design invariant for dictionary entries—iskvobj is always set to true for kvobj entries stored in the main dictionary. Adding a defensive NULL check here would be inconsistent with the established pattern across the codebase and unnecessary given this guarantee. No changes required.

src/module.c (1)

5454-5456: LGTM — updated call matches new hashTypeDelete signature.
No issues found with the new two-parameter usage.

src/defrag.c (1)

486-492: LGTM: correct use of plink when updating hash dict keys.

src/server.c (3)

321-333: dictSdsCompareKV update looks consistent with key extraction.

The signature and sds-to-sds compare logic line up with the new keyFromStoredKey flow.


604-615: LGTM: keyFromStoredKey wired for both db and expires dicts.

This aligns the dict types with the new stored-key extraction path and keeps lookups consistent.

Also applies to: 618-629


316-319: Add a defensive guard or assert the kvobj invariant.

kvGetKey returns NULL when kv->iskvobj is false, which would cause dictSdsHash/sdslen to crash. Confirm that all entries stored in dbDictType/dbExpiresDictType are guaranteed to have iskvobj=true, or add an assertion to catch violations:

Defensive guard
static const void *kvGetKey(const void *kv) {
    sds sdsKey = kvobjGetKey((kvobj *) kv);
+   serverAssert(sdsKey != NULL);
    return sdsKey;
}
src/dict.h (5)

55-66: LGTM! Clear documentation of the stored-key concept.

The documentation clearly explains the distinction between lookup keys and stored-keys, and when the keyFromStoredKey callback is needed. The __stored_key marker macro provides good self-documenting annotations for API consumers.


94-97: LGTM! Consistent annotation of callback signatures.

The __stored_key annotation on keyDup and keyDestructor correctly indicates these callbacks receive stored-key objects rather than lookup keys.


128-134: LGTM! Well-designed callback for stored-key extraction.

The keyFromStoredKey callback placement after the flags and the accompanying documentation clearly explains its purpose for no_value=1 dictionaries.


157-157: Type change from bit-field to int16_t affects struct layout.

This changes pauseAutoResize from a bit-field to a signed int16_t. The comment mentions negative values indicate coding errors, which is a reasonable defensive check. The padding optimization note in the struct comment (line 155) supports this change.


239-242: LGTM! Consistent __stored_key annotation across public API.

All insertion and mutation APIs (dictAdd, dictAddRaw, dictAddOrFind, dictReplace, dictSetKeyAtLink, dictSetKey) are correctly annotated to indicate they accept stored-key objects.

Also applies to: 288-288, 291-291

src/dict.c (9)

124-126: LGTM! Minor formatting adjustment.

The spacing change in siphash call is cosmetic.


339-364: LGTM! Correct stored-key handling during rehashing.

The rehashing logic properly extracts the lookup key via dictStoredKey2Key before computing the hash for the new bucket, while preserving the original storedKey for storage in the destination bucket.


526-536: LGTM! Stored-key extraction before lookup.

dictAddRaw correctly extracts the lookup key via dictStoredKey2Key for dictFindLinkForInsert, then passes the original stored-key to dictInsertKeyAtLink after optional duplication.


636-650: LGTM! Consistent stored-key extraction in delete path.

dictGenericDelete properly extracts the lookup key from each entry via dictStoredKey2Key for comparison against the search key.


779-802: LGTM! Stored-key extraction in lookup internals.

dictFindLinkInternal correctly extracts the lookup key from visited entries for comparison while iterating the hash chain.


887-914: LGTM! Stored-key handling in dictSetKeyAtLink.

The function correctly uses dictStoredKey2Key when looking up the bucket after table reallocation (line 902) and when finding an existing link (line 914).


964-973: LGTM! Stored-key extraction in two-phase unlink.

dictTwoPhaseUnlinkFind properly extracts keys for comparison during the search loop.


1748-1769: LGTM! Stored-key extraction in insert path lookup.

dictFindLinkForInsert correctly extracts keys from existing entries via dictStoredKey2Key for duplicate detection.


1807-1809: LGTM! Clean dictGetHash implementation.

The inline comment about compiler optimization is helpful, and the implementation correctly delegates to the type's hash function.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +97 to 108
static const void *dictStoredKey2Key(dict *d, const void *key __stored_key) {
return (d->type->keyFromStoredKey) ? d->type->keyFromStoredKey(key) : key;
}

/* Validate that stored-key to key conversion works correctly */
static int validateStoredKeyConversion(dict *d, const void *key __stored_key) {
const void *extracted = dictStoredKey2Key(d, key);
if (d->type->keyFromStoredKey) {
return extracted != NULL;
}
return extracted == key;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Remove or use validateStoredKeyConversion to fix build failure.

The function validateStoredKeyConversion is defined but never called, causing the build to fail with -Werror=unused-function. Either add calls to this validation function where appropriate (e.g., in dictAddRaw for debug builds) or remove it entirely.

Option 1: Remove the unused function
 static const void *dictStoredKey2Key(dict *d, const void *key __stored_key) {
     return (d->type->keyFromStoredKey) ? d->type->keyFromStoredKey(key) : key;
 }
-
-/* Validate that stored-key to key conversion works correctly */
-static int validateStoredKeyConversion(dict *d, const void *key __stored_key) {
-    const void *extracted = dictStoredKey2Key(d, key);
-    if (d->type->keyFromStoredKey) {
-        return extracted != NULL;
-    }
-    return extracted == key;
-}
Option 2: Guard with REDIS_TEST or use in assertions
+#ifdef REDIS_TEST
 /* Validate that stored-key to key conversion works correctly */
 static int validateStoredKeyConversion(dict *d, const void *key __stored_key) {
     const void *extracted = dictStoredKey2Key(d, key);
     if (d->type->keyFromStoredKey) {
         return extracted != NULL;
     }
     return extracted == key;
 }
+#endif
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static const void *dictStoredKey2Key(dict *d, const void *key __stored_key) {
return (d->type->keyFromStoredKey) ? d->type->keyFromStoredKey(key) : key;
}
/* Validate that stored-key to key conversion works correctly */
static int validateStoredKeyConversion(dict *d, const void *key __stored_key) {
const void *extracted = dictStoredKey2Key(d, key);
if (d->type->keyFromStoredKey) {
return extracted != NULL;
}
return extracted == key;
}
static const void *dictStoredKey2Key(dict *d, const void *key __stored_key) {
return (d->type->keyFromStoredKey) ? d->type->keyFromStoredKey(key) : key;
}
Suggested change
static const void *dictStoredKey2Key(dict *d, const void *key __stored_key) {
return (d->type->keyFromStoredKey) ? d->type->keyFromStoredKey(key) : key;
}
/* Validate that stored-key to key conversion works correctly */
static int validateStoredKeyConversion(dict *d, const void *key __stored_key) {
const void *extracted = dictStoredKey2Key(d, key);
if (d->type->keyFromStoredKey) {
return extracted != NULL;
}
return extracted == key;
}
static const void *dictStoredKey2Key(dict *d, const void *key __stored_key) {
return (d->type->keyFromStoredKey) ? d->type->keyFromStoredKey(key) : key;
}
`#ifdef` REDIS_TEST
/* Validate that stored-key to key conversion works correctly */
static int validateStoredKeyConversion(dict *d, const void *key __stored_key) {
const void *extracted = dictStoredKey2Key(d, key);
if (d->type->keyFromStoredKey) {
return extracted != NULL;
}
return extracted == key;
}
`#endif`
🧰 Tools
🪛 GitHub Actions: CI

[error] 102-102: Build failed due to -Werror: unused function 'validateStoredKeyConversion' in src/dict.c. Command: make REDIS_CFLAGS='-Werror' BUILD_TLS=yes.

🪛 GitHub Actions: External Server Tests

[error] 102-102: validateStoredKeyConversion defined but not used. (-Werror=unused-function)

🤖 Prompt for AI Agents
In `@src/dict.c` around lines 97 - 108, The function validateStoredKeyConversion
is defined but unused, causing -Werror=unused-function; either remove the
function entirely or invoke it in appropriate places (e.g., call
validateStoredKeyConversion(d, key) from dictAddRaw during debug/test builds or
wrap its use under REDIS_TEST) and/or replace it with an assertion that calls
dictStoredKey2Key(d, key) to validate behavior; update references to
dictStoredKey2Key and ensure any added call is only in non-production builds
(DEBUG/REDIS_TEST) to avoid runtime overhead.

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.

1 participant