Skip to content

Commit bb9e0e7

Browse files
pmcfadinclaude
andcommitted
fix: suppress IN_MEMORY_SORTING and ZERO_FILTER astrapy warnings on video listing
Adds `suppress_astrapy_warnings` context manager to `db_helpers.py` and applies it around the `find()` call in `list_videos_with_query()` to silence pre-existing performance-advisory warnings that astrapy emits when listing all videos without a partition key filter. Documented in GitHub issue #21. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent adc4b15 commit bb9e0e7

File tree

3 files changed

+98
-20
lines changed

3 files changed

+98
-20
lines changed

app/services/video_service.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -479,19 +479,23 @@ async def list_videos_with_query(
479479
span.set_attribute("page", page)
480480
span.set_attribute("page_size", page_size)
481481

482-
cursor = db_table.find(
483-
filter=query_filter, skip=skip, limit=page_size, sort=sort_options
484-
)
482+
from app.utils.db_helpers import safe_count, suppress_astrapy_warnings
483+
484+
with suppress_astrapy_warnings(
485+
"ZERO_FILTER_OPERATIONS",
486+
"IN_MEMORY_SORTING",
487+
):
488+
cursor = db_table.find(
489+
filter=query_filter, skip=skip, limit=page_size, sort=sort_options
490+
)
485491

486-
docs: List[Dict[str, Any]] = []
487-
if hasattr(cursor, "to_list"):
488-
docs = await cursor.to_list()
489-
else: # Stub collection path
490-
docs = cursor # type: ignore[assignment]
492+
docs: List[Dict[str, Any]] = []
493+
if hasattr(cursor, "to_list"):
494+
docs = await cursor.to_list()
495+
else: # Stub collection path
496+
docs = cursor # type: ignore[assignment]
491497

492498
# Use helper that gracefully degrades on tables
493-
from app.utils.db_helpers import safe_count
494-
495499
total_items = await safe_count(
496500
db_table,
497501
query_filter=query_filter,

app/utils/db_helpers.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,32 @@
1414
"""
1515

1616
import logging
17-
from typing import Any, Dict
17+
from contextlib import contextmanager
18+
from typing import Any, Dict, Iterator
1819

1920
from astrapy.exceptions.data_api_exceptions import DataAPIResponseException # type: ignore
2021

21-
__all__ = ["safe_count"]
22+
__all__ = ["safe_count", "suppress_astrapy_warnings"]
2223

2324
_ASTRAPY_LOGGER = logging.getLogger("astrapy.utils.api_commander")
2425

2526

26-
class _SuppressUnsupportedTableCommand(logging.Filter):
27-
"""Drop UNSUPPORTED_TABLE_COMMAND WARNING records from the astrapy logger.
27+
class _SuppressAstrapyWarnings(logging.Filter):
28+
"""Drop WARNING records whose message contains any of the given substrings.
2829
29-
astrapy emits a WARNING before raising DataAPIResponseException. For the
30-
expected case of ``countDocuments`` on a CQL table we catch and handle the
31-
exception ourselves, so the warning is noise rather than signal.
30+
astrapy emits WARNINGs for certain operations that we handle or expect
31+
(e.g. ``UNSUPPORTED_TABLE_COMMAND`` on tables, ``ZERO_FILTER_OPERATIONS``
32+
for unfiltered queries). This filter suppresses only the specified codes
33+
so legitimate warnings still surface.
3234
"""
3335

36+
def __init__(self, substrings: frozenset[str]) -> None:
37+
super().__init__()
38+
self._substrings = substrings
39+
3440
def filter(self, record: logging.LogRecord) -> bool:
35-
return "UNSUPPORTED_TABLE_COMMAND" not in record.getMessage()
41+
msg = record.getMessage()
42+
return not any(s in msg for s in self._substrings)
3643

3744

3845
async def safe_count(
@@ -48,7 +55,7 @@ async def safe_count(
4855
an exception. The same applies to stub collections used in unit-tests.
4956
"""
5057

51-
_filter = _SuppressUnsupportedTableCommand()
58+
_filter = _SuppressAstrapyWarnings(frozenset({"UNSUPPORTED_TABLE_COMMAND"}))
5259
_ASTRAPY_LOGGER.addFilter(_filter)
5360
try:
5461
return await db_table.count_documents(filter=query_filter, upper_bound=10**9)
@@ -61,3 +68,21 @@ async def safe_count(
6168
return fallback_len
6269
finally:
6370
_ASTRAPY_LOGGER.removeFilter(_filter)
71+
72+
73+
@contextmanager
74+
def suppress_astrapy_warnings(*warning_codes: str) -> Iterator[None]:
75+
"""Temporarily suppress astrapy warnings matching any of *warning_codes*.
76+
77+
Usage::
78+
79+
with suppress_astrapy_warnings("ZERO_FILTER_OPERATIONS", "IN_MEMORY_SORTING"):
80+
cursor = db_table.find(...)
81+
docs = await cursor.to_list()
82+
"""
83+
_filter = _SuppressAstrapyWarnings(frozenset(warning_codes))
84+
_ASTRAPY_LOGGER.addFilter(_filter)
85+
try:
86+
yield
87+
finally:
88+
_ASTRAPY_LOGGER.removeFilter(_filter)

tests/utils/test_db_helpers.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from astrapy.exceptions.data_api_exceptions import DataAPIResponseException # type: ignore[import]
1111

12-
from app.utils.db_helpers import safe_count
12+
from app.utils.db_helpers import safe_count, suppress_astrapy_warnings
1313

1414
_ASTRAPY_LOGGER = "astrapy.utils.api_commander"
1515

@@ -96,3 +96,52 @@ async def _fake_count_documents(*args, **kwargs):
9696
assert unsupported_warnings == [], (
9797
"safe_count should suppress astrapy's UNSUPPORTED_TABLE_COMMAND warning"
9898
)
99+
100+
101+
# ---------------------------------------------------------------------------
102+
# suppress_astrapy_warnings context manager
103+
# ---------------------------------------------------------------------------
104+
105+
106+
def test_suppress_astrapy_warnings_suppresses_matching_codes(caplog):
107+
"""Warnings matching any of the specified codes are suppressed."""
108+
astrapy_logger = logging.getLogger(_ASTRAPY_LOGGER)
109+
110+
with caplog.at_level(logging.WARNING, logger=_ASTRAPY_LOGGER):
111+
with suppress_astrapy_warnings("ZERO_FILTER_OPERATIONS", "IN_MEMORY_SORTING"):
112+
astrapy_logger.warning("ZERO_FILTER_OPERATIONS on table videos")
113+
astrapy_logger.warning("IN_MEMORY_SORTING due to non-partition key")
114+
115+
matching = [
116+
r for r in caplog.records
117+
if ("ZERO_FILTER_OPERATIONS" in r.getMessage()
118+
or "IN_MEMORY_SORTING" in r.getMessage())
119+
and r.levelno >= logging.WARNING
120+
]
121+
assert matching == [], "suppress_astrapy_warnings should suppress matching warnings"
122+
123+
124+
def test_suppress_astrapy_warnings_passes_unrelated_warnings(caplog):
125+
"""Warnings that don't match any specified code still appear."""
126+
astrapy_logger = logging.getLogger(_ASTRAPY_LOGGER)
127+
128+
with caplog.at_level(logging.WARNING, logger=_ASTRAPY_LOGGER):
129+
with suppress_astrapy_warnings("ZERO_FILTER_OPERATIONS"):
130+
astrapy_logger.warning("SOMETHING_ELSE happened")
131+
132+
unrelated = [
133+
r for r in caplog.records
134+
if "SOMETHING_ELSE" in r.getMessage()
135+
]
136+
assert len(unrelated) == 1, "Unrelated warnings must not be suppressed"
137+
138+
139+
def test_suppress_astrapy_warnings_removes_filter_after_exit():
140+
"""The filter is removed from the logger when the context exits."""
141+
astrapy_logger = logging.getLogger(_ASTRAPY_LOGGER)
142+
baseline = len(astrapy_logger.filters)
143+
144+
with suppress_astrapy_warnings("ZERO_FILTER_OPERATIONS"):
145+
assert len(astrapy_logger.filters) == baseline + 1
146+
147+
assert len(astrapy_logger.filters) == baseline

0 commit comments

Comments
 (0)