Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/actioncache/caches/with_cache.py: 78.72%
47 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-10-04 17:48 +0000
« prev ^ index » next coverage.py v7.4.1, created at 2024-10-04 17:48 +0000
1# Copyright (C) 2020 Bloomberg LP
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# <http://www.apache.org/licenses/LICENSE-2.0>
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
16from contextlib import ExitStack
17from typing import Tuple
19from buildgrid._protos.build.bazel.remote.execution.v2.remote_execution_pb2 import ActionResult, Digest
20from buildgrid.server.actioncache.caches.action_cache_abc import ActionCacheABC
21from buildgrid.server.exceptions import NotFoundError
22from buildgrid.server.logging import buildgrid_logger
24LOGGER = buildgrid_logger(__name__)
27class WithCacheActionCache(ActionCacheABC):
28 """
29 An ActionCache that first makes use of a storage and fallback cache
31 """
33 def __init__(
34 self, cache: ActionCacheABC, fallback: ActionCacheABC, allow_updates: bool, cache_failed_actions: bool
35 ) -> None:
36 """Initialise a new Action Cache with a fallback cache.
38 Args:
39 cache (ActionCacheABC): Local cache backend instance to be used.
40 fallback(ActionCacheABC): Fallback backend instance to be used
41 allow_updates(bool): Allow updates pushed to the Action Cache.
42 Defaults to ``True``.
43 cache_failed_actions(bool): Whether to store failed (non-zero exit
44 code) actions. Default to ``True``.
45 """
46 super().__init__(allow_updates=allow_updates)
47 self._stack = ExitStack()
48 self._cache_failed_actions = cache_failed_actions
49 self._cache = cache
50 self._fallback = fallback
52 def start(self) -> None:
53 self._stack.enter_context(self._cache)
54 self._stack.enter_context(self._fallback)
56 def stop(self) -> None:
57 self._stack.close()
59 def get_action_result(self, action_digest: Digest) -> ActionResult:
60 """Retrieves the cached result for an Action.
62 Will first attempt to retrieve result from cache and then fallback. A
63 NotFoundError is raised if both cache and fallback return None.
65 Args:
66 action_digest (Digest): The digest of the Action to retrieve the
67 cached result of.
69 """
70 cache_result = None
71 fallback_result = None
72 key = self._get_key(action_digest)
73 try:
74 cache_result = self._cache.get_action_result(action_digest)
75 except NotFoundError:
76 pass
77 except Exception:
78 LOGGER.exception("Unexpected error in cache get_action_result.", tags=dict(digest=action_digest))
80 if cache_result is None:
81 fallback_result = self._fallback.get_action_result(action_digest)
82 else:
83 return cache_result
85 if fallback_result is not None:
86 self._cache.update_action_result(action_digest, fallback_result)
87 return fallback_result
89 raise NotFoundError(f"Key not found: {key}")
91 def update_action_result(self, action_digest: Digest, action_result: ActionResult) -> None:
92 """Stores a result for an Action in the cache.
94 Will attempt to store result in cache, and then in fallback cache. If
95 the result has a non-zero exit code and `cache_failed_actions` is False
96 for this cache, the result is not cached.
98 Args:
99 action_digest (Digest): The digest of the Action whose result is
100 being cached.
101 action_result (ActionResult): The result to cache for the given
102 Action digest.
104 """
105 if self._cache_failed_actions or action_result.exit_code == 0:
106 if not self._allow_updates:
107 raise NotImplementedError("Updating cache not allowed")
109 try:
110 self._cache.update_action_result(action_digest, action_result)
112 except Exception:
113 LOGGER.warning("Failed to cache action.", tags=dict(digest=action_digest), exc_info=True)
115 self._fallback.update_action_result(action_digest, action_result)
117 def _get_key(self, action_digest: Digest) -> Tuple[str, int]:
118 """Get a hashable cache key from a given Action digest.
120 Args:
121 action_digest (Digest): The digest to produce a cache key for.
123 """
124 return (action_digest.hash, action_digest.size_bytes)