Skip to content

Commit d7ccf06

Browse files
fix(django): Improve logic for classifying cache hits and misses (#5029)
Determine cache hits and misses independently for `django.core.cache.get()` and `django.core.cache.get_many()`. When calling `get_many()`, only register a cache miss when the returned value is an empty dictionary. When calling `get()`, register a cache miss when - the return value is `None` and no default value is provided; or - the return value is equal to a provided default value. Closes #5027
1 parent 64c145f commit d7ccf06

File tree

2 files changed

+88
-7
lines changed

2 files changed

+88
-7
lines changed

sentry_sdk/integrations/django/caching.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ def _instrument_call(
4545
):
4646
# type: (CacheHandler, str, Callable[..., Any], tuple[Any, ...], dict[str, Any], Optional[str], Optional[int]) -> Any
4747
is_set_operation = method_name.startswith("set")
48-
is_get_operation = not is_set_operation
48+
is_get_method = method_name == "get"
49+
is_get_many_method = method_name == "get_many"
4950

5051
op = OP.CACHE_PUT if is_set_operation else OP.CACHE_GET
5152
description = _get_span_description(method_name, args, kwargs)
@@ -69,8 +70,20 @@ def _instrument_call(
6970
span.set_data(SPANDATA.CACHE_KEY, key)
7071

7172
item_size = None
72-
if is_get_operation:
73-
if value:
73+
if is_get_many_method:
74+
if value != {}:
75+
item_size = len(str(value))
76+
span.set_data(SPANDATA.CACHE_HIT, True)
77+
else:
78+
span.set_data(SPANDATA.CACHE_HIT, False)
79+
elif is_get_method:
80+
default_value = None
81+
if len(args) >= 2:
82+
default_value = args[1]
83+
elif "default" in kwargs:
84+
default_value = kwargs["default"]
85+
86+
if value != default_value:
7487
item_size = len(str(value))
7588
span.set_data(SPANDATA.CACHE_HIT, True)
7689
else:

tests/integrations/django/test_cache_module.py

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,8 @@ def test_cache_spans_middleware(
223223
assert second_event["spans"][0]["data"]["cache.key"][0].startswith(
224224
"views.decorators.cache.cache_header."
225225
)
226-
assert not second_event["spans"][0]["data"]["cache.hit"]
227-
assert "cache.item_size" not in second_event["spans"][0]["data"]
226+
assert second_event["spans"][0]["data"]["cache.hit"]
227+
assert second_event["spans"][0]["data"]["cache.item_size"] == 2
228228
# second_event - cache.get 2
229229
assert second_event["spans"][1]["op"] == "cache.get"
230230
assert second_event["spans"][1]["description"].startswith(
@@ -501,14 +501,76 @@ def test_cache_spans_item_size(sentry_init, client, capture_events, use_django_c
501501

502502
assert len(second_event["spans"]) == 2
503503
assert second_event["spans"][0]["op"] == "cache.get"
504-
assert not second_event["spans"][0]["data"]["cache.hit"]
505-
assert "cache.item_size" not in second_event["spans"][0]["data"]
504+
assert second_event["spans"][0]["data"]["cache.hit"]
505+
assert second_event["spans"][0]["data"]["cache.item_size"] == 2
506506

507507
assert second_event["spans"][1]["op"] == "cache.get"
508508
assert second_event["spans"][1]["data"]["cache.hit"]
509509
assert second_event["spans"][1]["data"]["cache.item_size"] == 58
510510

511511

512+
@pytest.mark.forked
513+
@pytest_mark_django_db_decorator()
514+
def test_cache_spans_get_custom_default(
515+
sentry_init, capture_events, use_django_caching
516+
):
517+
sentry_init(
518+
integrations=[
519+
DjangoIntegration(
520+
cache_spans=True,
521+
middleware_spans=False,
522+
signals_spans=False,
523+
)
524+
],
525+
traces_sample_rate=1.0,
526+
)
527+
events = capture_events()
528+
529+
id = os.getpid()
530+
531+
from django.core.cache import cache
532+
533+
with sentry_sdk.start_transaction():
534+
cache.set(f"S{id}", "Sensitive1")
535+
cache.set(f"S{id + 1}", "")
536+
537+
cache.get(f"S{id}", "null")
538+
cache.get(f"S{id}", default="null")
539+
540+
cache.get(f"S{id + 1}", "null")
541+
cache.get(f"S{id + 1}", default="null")
542+
543+
cache.get(f"S{id + 2}", "null")
544+
cache.get(f"S{id + 2}", default="null")
545+
546+
(transaction,) = events
547+
assert len(transaction["spans"]) == 8
548+
549+
assert transaction["spans"][0]["op"] == "cache.put"
550+
assert transaction["spans"][0]["description"] == f"S{id}"
551+
552+
assert transaction["spans"][1]["op"] == "cache.put"
553+
assert transaction["spans"][1]["description"] == f"S{id + 1}"
554+
555+
for span in (transaction["spans"][2], transaction["spans"][3]):
556+
assert span["op"] == "cache.get"
557+
assert span["description"] == f"S{id}"
558+
assert span["data"]["cache.hit"]
559+
assert span["data"]["cache.item_size"] == 10
560+
561+
for span in (transaction["spans"][4], transaction["spans"][5]):
562+
assert span["op"] == "cache.get"
563+
assert span["description"] == f"S{id + 1}"
564+
assert span["data"]["cache.hit"]
565+
assert span["data"]["cache.item_size"] == 0
566+
567+
for span in (transaction["spans"][6], transaction["spans"][7]):
568+
assert span["op"] == "cache.get"
569+
assert span["description"] == f"S{id + 2}"
570+
assert not span["data"]["cache.hit"]
571+
assert "cache.item_size" not in span["data"]
572+
573+
512574
@pytest.mark.forked
513575
@pytest_mark_django_db_decorator()
514576
def test_cache_spans_get_many(sentry_init, capture_events, use_django_caching):
@@ -538,24 +600,30 @@ def test_cache_spans_get_many(sentry_init, capture_events, use_django_caching):
538600

539601
assert transaction["spans"][0]["op"] == "cache.get"
540602
assert transaction["spans"][0]["description"] == f"S{id}, S{id + 1}"
603+
assert not transaction["spans"][0]["data"]["cache.hit"]
541604

542605
assert transaction["spans"][1]["op"] == "cache.get"
543606
assert transaction["spans"][1]["description"] == f"S{id}"
607+
assert not transaction["spans"][1]["data"]["cache.hit"]
544608

545609
assert transaction["spans"][2]["op"] == "cache.get"
546610
assert transaction["spans"][2]["description"] == f"S{id + 1}"
611+
assert not transaction["spans"][2]["data"]["cache.hit"]
547612

548613
assert transaction["spans"][3]["op"] == "cache.put"
549614
assert transaction["spans"][3]["description"] == f"S{id}"
550615

551616
assert transaction["spans"][4]["op"] == "cache.get"
552617
assert transaction["spans"][4]["description"] == f"S{id}, S{id + 1}"
618+
assert transaction["spans"][4]["data"]["cache.hit"]
553619

554620
assert transaction["spans"][5]["op"] == "cache.get"
555621
assert transaction["spans"][5]["description"] == f"S{id}"
622+
assert transaction["spans"][5]["data"]["cache.hit"]
556623

557624
assert transaction["spans"][6]["op"] == "cache.get"
558625
assert transaction["spans"][6]["description"] == f"S{id + 1}"
626+
assert not transaction["spans"][6]["data"]["cache.hit"]
559627

560628

561629
@pytest.mark.forked

0 commit comments

Comments
 (0)