Coverage for python / lsst / daf / butler / registry / _caching_context.py: 49%
37 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-30 08:41 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-30 08:41 +0000
1# This file is part of daf_butler.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (http://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# This software is dual licensed under the GNU General Public License and also
10# under a 3-clause BSD license. Recipients may choose which of these licenses
11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
12# respectively. If you choose the GPL option then the following text applies
13# (but note that there is still no warranty even if you opt for BSD instead):
14#
15# This program is free software: you can redistribute it and/or modify
16# it under the terms of the GNU General Public License as published by
17# the Free Software Foundation, either version 3 of the License, or
18# (at your option) any later version.
19#
20# This program is distributed in the hope that it will be useful,
21# but WITHOUT ANY WARRANTY; without even the implied warranty of
22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23# GNU General Public License for more details.
24#
25# You should have received a copy of the GNU General Public License
26# along with this program. If not, see <http://www.gnu.org/licenses/>.
28from __future__ import annotations
30from collections.abc import Callable, Iterator
31from contextlib import AbstractContextManager, contextmanager
32from typing import Generic, TypeVar
34__all__ = ["CachingContext"]
36from ._collection_record_cache import CollectionRecordCache
37from ._collection_summary_cache import CollectionSummaryCache
40class CachingContext:
41 """Collection of caches for various types of records retrieved from
42 database.
44 Notes
45 -----
46 Caching is usually disabled for most of the record types, but it can be
47 explicitly and temporarily enabled in some context (e.g. quantum graph
48 building) using Registry method. This class is a collection of cache
49 instances which will be `None` when caching is disabled. Instance of this
50 class is passed to the relevant managers that can use it to query or
51 populate caches when caching is enabled.
52 """
54 def __init__(self) -> None:
55 self._collection_records = _CacheToggle(CollectionRecordCache)
56 self._collection_summaries = _CacheToggle(CollectionSummaryCache)
58 def enable_collection_record_cache(self) -> AbstractContextManager[None]:
59 """Enable the collection record cache.
61 Notes
62 -----
63 When this cache is enabled, any changes made by other processes to
64 collections in the database may not be visible.
65 """
66 return self._collection_records.enable()
68 def enable_collection_summary_cache(self) -> AbstractContextManager[None]:
69 """Enable the collection summary cache.
71 Notes
72 -----
73 When this cache is enabled, changes made by other processes to
74 collections in the database may not be visible.
76 When the collection summary cache is enabled, the performance of
77 database lookups for summaries changes. Summaries will be aggressively
78 fetched for all dataset types in the collections, which can cause
79 significantly more rows to be returned than when the cache is disabled.
80 This should only be enabled when you know that you will be doing many
81 summary lookups for the same collections.
82 """
83 return self._collection_summaries.enable()
85 @property
86 def collection_records(self) -> CollectionRecordCache | None:
87 """Cache for collection records (`CollectionRecordCache`)."""
88 return self._collection_records.cache
90 @property
91 def collection_summaries(self) -> CollectionSummaryCache | None:
92 """Cache for collection summary records (`CollectionSummaryCache`)."""
93 return self._collection_summaries.cache
96_T = TypeVar("_T")
99class _CacheToggle(Generic[_T]):
100 """Utility class to track nested enable/disable calls for a cache."""
102 def __init__(self, enable_function: Callable[[], _T]):
103 self.cache: _T | None = None
104 self._enable_function = enable_function
105 self._depth = 0
107 @contextmanager
108 def enable(self) -> Iterator[None]:
109 """Context manager to enable the cache. This context may be nested any
110 number of times, and the cache will only be disabled once all callers
111 have exited the context manager.
112 """
113 self._depth += 1
114 try:
115 if self._depth == 1:
116 self.cache = self._enable_function()
117 yield
118 finally:
119 self._depth -= 1
120 if self._depth == 0:
121 self.cache = None