|
16 | 16 | # under the License. |
17 | 17 | from __future__ import annotations |
18 | 18 |
|
| 19 | +import math |
19 | 20 | import time |
20 | 21 | from concurrent.futures import ThreadPoolExecutor |
21 | 22 | from unittest.mock import MagicMock, patch |
@@ -259,14 +260,24 @@ def test_lru_cache_enabled_with_cache_size(self): |
259 | 260 | assert isinstance(dag_bag._dags, LRUCache) |
260 | 261 |
|
261 | 262 | def test_ttl_cache_enabled_with_cache_size_and_ttl(self): |
262 | | - """Test that TTL cache is enabled when both cache_size and cache_ttl are provided.""" |
| 263 | + """Test that a bounded TTL cache is used when both cache_size and cache_ttl are provided.""" |
263 | 264 | dag_bag = DBDagBag(cache_size=10, cache_ttl=60) |
264 | 265 | assert dag_bag._use_cache is True |
265 | 266 | assert isinstance(dag_bag._dags, TTLCache) |
| 267 | + assert dag_bag._dags.maxsize == 10 |
266 | 268 |
|
267 | | - def test_zero_cache_size_uses_unbounded_dict(self): |
268 | | - """Test that cache_size=0 uses unbounded dict (same as no caching).""" |
269 | | - dag_bag = DBDagBag(cache_size=0, cache_ttl=60) |
| 269 | + @pytest.mark.parametrize("cache_size", [0, None]) |
| 270 | + def test_ttl_only_without_size_cap(self, cache_size): |
| 271 | + """Test that a positive cache_ttl with no size cap gives a TTL cache with maxsize=inf.""" |
| 272 | + dag_bag = DBDagBag(cache_size=cache_size, cache_ttl=60) |
| 273 | + assert dag_bag._use_cache is True |
| 274 | + assert isinstance(dag_bag._dags, TTLCache) |
| 275 | + assert dag_bag._dags.maxsize == math.inf |
| 276 | + |
| 277 | + @pytest.mark.parametrize("cache_ttl", [None, 0]) |
| 278 | + def test_zero_cache_size_uses_unbounded_dict(self, cache_ttl): |
| 279 | + """Test that cache_size=0 without a TTL uses an unbounded dict (same as no caching).""" |
| 280 | + dag_bag = DBDagBag(cache_size=0, cache_ttl=cache_ttl) |
270 | 281 | assert dag_bag._use_cache is False |
271 | 282 | assert isinstance(dag_bag._dags, dict) |
272 | 283 |
|
@@ -310,6 +321,21 @@ def test_ttl_cache_expiry(self): |
310 | 321 | with time_machine.travel("2025-01-01 00:00:02", tick=False): |
311 | 322 | assert dag_bag._dags.get("test_version_id") is None |
312 | 323 |
|
| 324 | + def test_ttl_only_evicts_by_ttl_not_size(self): |
| 325 | + """An unbounded (maxsize=inf) TTL cache keeps every entry until it expires by age.""" |
| 326 | + dag_bag = DBDagBag(cache_size=0, cache_ttl=1) |
| 327 | + assert dag_bag._dags.maxsize == math.inf |
| 328 | + dag_bag._dags = TTLCache(maxsize=math.inf, ttl=1, timer=time.time) |
| 329 | + |
| 330 | + with time_machine.travel("2025-01-01 00:00:00", tick=False): |
| 331 | + for i in range(500): |
| 332 | + dag_bag._dags[f"version_{i}"] = MagicMock() |
| 333 | + assert len(dag_bag._dags) == 500 |
| 334 | + |
| 335 | + with time_machine.travel("2025-01-01 00:00:02", tick=False): |
| 336 | + assert dag_bag._dags.get("version_0") is None |
| 337 | + assert len(dag_bag._dags) == 0 |
| 338 | + |
313 | 339 | def test_lru_eviction(self): |
314 | 340 | """Test that LRU eviction works when cache is full.""" |
315 | 341 | dag_bag = DBDagBag(cache_size=2) |
|
0 commit comments