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