Coverage for /builds/BuildGrid/buildgrid/buildgrid/server/actioncache/caches/write_once_cache.py: 80.77%

26 statements  

« prev     ^ index     » next       coverage.py v7.4.1, created at 2025-05-21 15:45 +0000

1# Copyright (C) 2019, 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. 

14 

15 

16""" 

17Write Once Action Cache 

18======================= 

19 

20An ActionCache backend implementation which only allows each digest to 

21be written to once. Any subsequent requests to cache a result for the 

22same digests will not be permitted. 

23 

24This can be used to wrap a different ActionCache implementation to provide 

25immutability of cache results. 

26 

27""" 

28 

29from buildgrid._protos.build.bazel.remote.execution.v2.remote_execution_pb2 import ActionResult, Digest 

30from buildgrid.server.actioncache.caches.action_cache_abc import ActionCacheABC 

31from buildgrid.server.exceptions import NotFoundError, UpdateNotAllowedError 

32from buildgrid.server.logging import buildgrid_logger 

33 

34LOGGER = buildgrid_logger(__name__) 

35 

36 

37class WriteOnceActionCache(ActionCacheABC): 

38 def __init__(self, action_cache: ActionCacheABC): 

39 super().__init__() 

40 self._action_cache = action_cache 

41 

42 def start(self) -> None: 

43 self._action_cache.start() 

44 

45 def stop(self) -> None: 

46 self._action_cache.stop() 

47 

48 @property 

49 def allow_updates(self) -> bool: 

50 return self._action_cache.allow_updates 

51 

52 def get_action_result(self, action_digest: Digest) -> ActionResult: 

53 """Retrieves the cached ActionResult for the given Action digest. 

54 

55 Args: 

56 action_digest (Digest): The digest of the Action to retrieve the 

57 cached result of. 

58 

59 Returns: 

60 The cached ActionResult matching the given digest or raises 

61 NotFoundError. 

62 

63 """ 

64 return self._action_cache.get_action_result(action_digest) 

65 

66 def update_action_result(self, action_digest: Digest, action_result: ActionResult) -> None: 

67 """Stores the result for a given digest in the cache. 

68 

69 If the digest already exists in the cache, then an UpdateNotAllowedError 

70 is raised instead of storing the result. 

71 

72 Args: 

73 action_digest (Digest): The digest of the Action whose result is 

74 being cached. 

75 action_result (ActionResult): The result to cache for the given 

76 Action digest. 

77 

78 """ 

79 try: 

80 self._action_cache.get_action_result(action_digest) 

81 # This should throw NotFoundError or actually exist 

82 LOGGER.warning( 

83 "Result already cached for action, WriteOnceActionCache won't overwrite it to the new action result.", 

84 tags=dict(digest=action_digest, action_result=action_result), 

85 ) 

86 

87 raise UpdateNotAllowedError( 

88 "Result already stored for this action digest;WriteOnceActionCache doesn't allow updates." 

89 ) 

90 except NotFoundError: 

91 self._action_cache.update_action_result(action_digest, action_result)