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-17 08:49 +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/>. 

27 

28from __future__ import annotations 

29 

30from collections.abc import Callable, Iterator 

31from contextlib import AbstractContextManager, contextmanager 

32from typing import Generic, TypeVar 

33 

34__all__ = ["CachingContext"] 

35 

36from ._collection_record_cache import CollectionRecordCache 

37from ._collection_summary_cache import CollectionSummaryCache 

38 

39 

40class CachingContext: 

41 """Collection of caches for various types of records retrieved from 

42 database. 

43 

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 """ 

53 

54 def __init__(self) -> None: 

55 self._collection_records = _CacheToggle(CollectionRecordCache) 

56 self._collection_summaries = _CacheToggle(CollectionSummaryCache) 

57 

58 def enable_collection_record_cache(self) -> AbstractContextManager[None]: 

59 """Enable the collection record cache. 

60 

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() 

67 

68 def enable_collection_summary_cache(self) -> AbstractContextManager[None]: 

69 """Enable the collection summary cache. 

70 

71 Notes 

72 ----- 

73 When this cache is enabled, changes made by other processes to 

74 collections in the database may not be visible. 

75 

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() 

84 

85 @property 

86 def collection_records(self) -> CollectionRecordCache | None: 

87 """Cache for collection records (`CollectionRecordCache`).""" 

88 return self._collection_records.cache 

89 

90 @property 

91 def collection_summaries(self) -> CollectionSummaryCache | None: 

92 """Cache for collection summary records (`CollectionSummaryCache`).""" 

93 return self._collection_summaries.cache 

94 

95 

96_T = TypeVar("_T") 

97 

98 

99class _CacheToggle(Generic[_T]): 

100 """Utility class to track nested enable/disable calls for a cache.""" 

101 

102 def __init__(self, enable_function: Callable[[], _T]): 

103 self.cache: _T | None = None 

104 self._enable_function = enable_function 

105 self._depth = 0 

106 

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