xref: /aosp_15_r20/external/federated-compute/fcp/client/cache/file_backed_resource_cache.h (revision 14675a029014e728ec732f129a32e299b2da0601)
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef FCP_CLIENT_CACHE_FILE_BACKED_RESOURCE_CACHE_H_
18 #define FCP_CLIENT_CACHE_FILE_BACKED_RESOURCE_CACHE_H_
19 
20 #include <filesystem>
21 #include <memory>
22 #include <optional>
23 #include <string>
24 #include <utility>
25 
26 #include "google/protobuf/any.pb.h"
27 #include "absl/status/status.h"
28 #include "absl/status/statusor.h"
29 #include "absl/strings/cord.h"
30 #include "fcp/base/clock.h"
31 #include "fcp/client/cache/cache_manifest.pb.h"
32 #include "fcp/client/cache/resource_cache.h"
33 #include "fcp/client/log_manager.h"
34 #include "protostore/file-storage.h"
35 #include "protostore/proto-data-store.h"
36 
37 namespace fcp {
38 namespace client {
39 namespace cache {
40 
41 /**
42  * A FileBackedResourceCache is a ResourceCache implementation where each
43  * resource payload is stored as an individual file in a directory, along with a
44  * ProtoDataStore manifest that tracks each entry.
45  *
46  * FileBackedResourceCache is thread safe.
47  */
48 class FileBackedResourceCache : public ResourceCache {
49  public:
50   // The CacheManifest will be created in
51   // <base directory>/fcp/cache_manifest.pb.
52 
53   // Factory method to create FileBackedResourceCache. The provided cache dir is
54   // the absolute path for storing cached files, and the provided base dir is
55   // the absolute path for longer term storage. FileBackedResourceCache will
56   // attempt to create subdirectories and files, so the directory must grant
57   // read/write access.
58   //
59   // A FileBackedResourceCache will not store any resources larger than
60   // `max_cache_size_bytes` / 2.
61   //
62   // Deletes any stored resources past expiry.
63   static absl::StatusOr<std::unique_ptr<FileBackedResourceCache>> Create(
64       absl::string_view base_dir, absl::string_view cache_dir,
65       LogManager* log_manager, fcp::Clock* clock, int64_t max_cache_size_bytes);
66 
67   // Implementation of `ResourceCache::Put`.
68   //
69   // If storing `resource` pushes the size of the cache directory over
70   // `max_cache_size_bytes`, entries with the oldest last_accessed_time will be
71   // deleted until the directory is under `max_cache_size_bytes` Returns Ok on
72   // success. On error, returns:
73   // - INTERNAL - unexpected error.
74   // - INVALID_ARGUMENT - if max_age is in the past.
75   // - RESOURCE_EXHAUSTED - if resource bytes is bigger than
76   //   `max_cache_size_bytes` / 2.
77   absl::Status Put(absl::string_view cache_id, const absl::Cord& resource,
78                    const google::protobuf::Any& metadata,
79                    absl::Duration max_age) override ABSL_LOCKS_EXCLUDED(mutex_);
80 
81   // Implementation of `ResourceCache::Get`.
82   absl::StatusOr<ResourceAndMetadata> Get(absl::string_view cache_id,
83                                           std::optional<absl::Duration> max_age)
84       override ABSL_LOCKS_EXCLUDED(mutex_);
85 
86   ~FileBackedResourceCache() override = default;
87 
88   // FileBackedResourceCache is neither copyable nor movable.
89   FileBackedResourceCache(const FileBackedResourceCache&) = delete;
90   FileBackedResourceCache& operator=(const FileBackedResourceCache&) = delete;
91 
92  private:
FileBackedResourceCache(std::unique_ptr<protostore::ProtoDataStore<CacheManifest>> pds,std::unique_ptr<protostore::FileStorage> storage,std::filesystem::path cache_dir_path,std::filesystem::path manifest_path,LogManager * log_manager,Clock * clock,const int64_t max_cache_size_bytes)93   FileBackedResourceCache(
94       std::unique_ptr<protostore::ProtoDataStore<CacheManifest>> pds,
95       std::unique_ptr<protostore::FileStorage> storage,
96       std::filesystem::path cache_dir_path, std::filesystem::path manifest_path,
97       LogManager* log_manager, Clock* clock, const int64_t max_cache_size_bytes)
98       : storage_(std::move(storage)),
99         pds_(std::move(pds)),
100         cache_dir_path_(cache_dir_path),
101         manifest_path_(manifest_path),
102         log_manager_(*log_manager),
103         clock_(*clock),
104         max_cache_size_bytes_(max_cache_size_bytes) {}
105 
106   absl::StatusOr<CacheManifest> ReadInternal()
107       ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
108   absl::Status WriteInternal(std::unique_ptr<CacheManifest> manifest)
109       ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
110 
111   // Initializes the CacheManifest ProtoDataStore db if necessesary, then runs
112   // CleanUp().
113   absl::Status Initialize() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
114 
115   // Deletes the cache manifest.
116   absl::Status DeleteManifest() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
117 
118   // TTLs any cached resources stored past their expiry, then deletes any
119   // stranded files without matching manifest entries, and any entries without
120   // matching resource files. If `reserved_space_bytes` is set, cleans up
121   // resources sorted by least recently used until the cache size is less than
122   // `max_cache_size_bytes_ - reserved_space_bytes`.
123   // This modifies the passed `manifest`.
124   absl::Status CleanUp(std::optional<int64_t> reserved_space_bytes,
125                        CacheManifest& manifest)
126       ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
127 
128   // Unused, but must be kept alive for longer than pds_.
129   std::unique_ptr<protostore::FileStorage> storage_;
130   std::unique_ptr<protostore::ProtoDataStore<CacheManifest>> pds_
131       ABSL_GUARDED_BY(mutex_);
132   const std::filesystem::path cache_dir_path_;
133   const std::filesystem::path manifest_path_;
134   LogManager& log_manager_;
135   Clock& clock_;
136   const int64_t max_cache_size_bytes_;
137   absl::Mutex mutex_;
138 };
139 
140 // Used by the class and in tests only.
141 namespace internal {}  // namespace internal
142 
143 }  // namespace cache
144 }  // namespace client
145 }  // namespace fcp
146 
147 #endif  // FCP_CLIENT_CACHE_FILE_BACKED_RESOURCE_CACHE_H_
148