Skip to content

Improve cache hit/miss reporting for cache.get() - #2418

Open
Harsh3006 wants to merge 8 commits into
django-commons:mainfrom
Harsh3006:fix-cache-get-hit-miss
Open

Improve cache hit/miss reporting for cache.get()#2418
Harsh3006 wants to merge 8 commits into
django-commons:mainfrom
Harsh3006:fix-cache-get-hit-miss

Conversation

@Harsh3006

@Harsh3006 Harsh3006 commented Jul 15, 2026

Copy link
Copy Markdown

Description

This change fixes incorrect cache hit/miss reporting in the Cache panel for cache.get().

Previously, the Cache panel determined whether a cache.get() call was a hit or a miss solely based on whether the returned value was None. This resulted in two incorrect scenarios:

A missing key with a supplied default (for example, cache.get("missing", "default")) was reported as a cache hit, even though the value was not retrieved from the cache.
A cached value of None was reported as a cache miss, even though the key existed in the cache.

The Cache panel now correctly distinguishes between a missing cache key and a cached value equal to the caller's supplied default while preserving the existing cache.get() behavior.

Fixes #2417

Checklist:

  • I have added the relevant tests for this change.
  • I have added an item to the Pending section of docs/changes.rst.

AI/LLM Usage

  • This PR includes code generated with the help of an AI/LLM

@Harsh3006
Harsh3006 force-pushed the fix-cache-get-hit-miss branch 3 times, most recently from 13bc758 to 7af7a62 Compare July 15, 2026 19:56
@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  debug_toolbar
  _compat.py
  apps.py
  middleware.py
  toolbar.py
  debug_toolbar/panels
  cache.py 127
  profiling.py
  redirects.py
  debug_toolbar/panels/sql
  views.py
Project Total  

This report was generated by python-coverage-comment-action

Comment thread debug_toolbar/panels/cache.py Outdated
Comment thread tests/panels/test_cache.py
@Harsh3006

Copy link
Copy Markdown
Author

@mgstabrani I noticed that CI was still flagging the self.misses += 1 branch for get_or_set() as uncovered. I've added a test for the None return path in a follow-up commit to close the remaining coverage gap.

No get_or_set() logic was changed; this only adds the missing test coverage.

@tim-schilling tim-schilling 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.

I appreciate the time and effort @Harsh3006, but I think this implementation isn't something we can adopt. I'm open to alternative solutions or ideas, but this as is, I'm not onboard with right now.

This changes what the user is doing and could have downstream effects in people's applications. For example, if someone has another observability tool implemented underneath the toolbar that counts the unique default values passed in, we would break that functionality. And it would break silently while being extremely difficult to debug. They would see some random memory address being passed as the default which would be hard to search on. They'd have to audit all the packages that do something related to caching to track it down to us, and when they do, I suspect they'll be a little upset 😅

Shorter version: The toolbar shouldn't be making changes to function calls without permission from the user.

@Harsh3006

Harsh3006 commented Aug 8, 2026

Copy link
Copy Markdown
Author

Thanks @tim-schilling for the explanation. That makes sense. I hadn't considered the effect on downstream instrumentation that may observe the original call arguments.

I spent some more time looking for an alternative that could solve this without modifying the actual cache.get() call, but I couldn't find a fully reliable solution. It seems this is a limitation of the current API: once cache.get() returns, there isn't enough information to determine whether the returned value came from the cache or from the supplied default.

One alternative I considered was comparing the returned value with the supplied default instead of checking for None. This would correctly identify a miss when the key is missing and a non-None default is provided. However, it only shits the ambiguity rather than eliminating it: it would incorrectly report a miss when the cached value happens to be equal to the supplied default.

I also cross-checked this approach against the current test suite, and as expected, these two tests fail:

  • test_cached_none_counts_as_hit
  • test_cached_value_equal_to_default_counts_as_hit

At this point, the sentinel approach seems to be the only approach I've found that can distinguish these cases reliably, but I understand the concern about modifying the arguments passed to the wrapped function.

@Harsh3006
Harsh3006 force-pushed the fix-cache-get-hit-miss branch from e2d3c9c to 91443f5 Compare August 10, 2026 19:58
@Harsh3006 Harsh3006 changed the title Fix incorrect cache hit/miss reporting for cache.get() Improve cache hit/miss reporting for cache.get() Aug 10, 2026
@Harsh3006

Harsh3006 commented Aug 10, 2026

Copy link
Copy Markdown
Author

@tim-schilling Here are the screenshots showing the template and documentation changes, including a note on the remaining ambiguity and a link to the open discussion.

Updated Cache Panel:

image image

Updated Documentation:
image

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.

Cache panel incorrectly reports hits and misses for cache.get() when a default value is supplied or None is cached

3 participants