| // Copyright 2024 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef COMPONENTS_PERSISTENT_CACHE_PERSISTENT_CACHE_H_ | |
| #define COMPONENTS_PERSISTENT_CACHE_PERSISTENT_CACHE_H_ | |
| #include <stdint.h> | |
| #include <memory> | |
| #include <optional> | |
| #include "base/component_export.h" | |
| #include "base/containers/span.h" | |
| #include "base/rand_util.h" | |
| #include "base/synchronization/lock.h" | |
| #include "base/thread_annotations.h" | |
| #include "base/timer/elapsed_timer.h" | |
| #include "base/types/expected.h" | |
| #include "components/persistent_cache/buffer_provider.h" | |
| #include "components/persistent_cache/entry_metadata.h" | |
| #include "components/persistent_cache/lock_state.h" | |
| namespace persistent_cache { | |
| class Backend; | |
| enum class Client; | |
| struct PendingBackend; | |
| enum class TransactionError; | |
| // Use PersistentCache to store and retrieve key-value pairs across processes or | |
| // threads. | |
| // | |
| // Example: | |
| // // Acquire a PendingBackend. | |
| // PendingBackend pending_backend = AcquirePendingBackend(); | |
| // auto persistent_cache = PersistentCache::Bind(std::move(pending_backend)); | |
| // if (!persistent_cache) { | |
| // // Handle error. | |
| // } | |
| // | |
| // // Add a key-value pair. | |
| // base::span<const uint8_t> key = base::byte_span_from_cstring("key"); | |
| // persistent_cache->Insert(key, base::byte_span_from_cstring("1")); | |
| // | |
| // // Retrieve a value. | |
| // { | |
| // base::HeapArray<uint8_t> content; | |
| // ASSIGN_OR_RETURN( | |
| // auto metadata, | |
| // persistent_cache->Find(key, [&content](size_t size) { | |
| // content = base::HeapArray<uint8_t>::Uninit(size); | |
| // return base::span(content); | |
| // }), | |
| // [](persistent_cache::TransactionError error) { | |
| // // Translate and handle error here. | |
| // }); | |
| // | |
| // if (metadata.has_value()) { | |
| // UseEntry(*std::move(metadata), std::move(content)); | |
| // } | |
| // } | |
| // | |
| // // Inserting again overwrites anything in there if present. | |
| // persistent_cache->Insert(key, base::byte_span_from_cstring("2")); | |
| // | |
| // | |
| // Error Handling and Recovery: | |
| // Operations can return a `TransactionError`, which dictates the required | |
| // recovery action. | |
| // | |
| // The error types are: | |
| // - `TransactionError::kTransient`: A recoverable backend error occurred. The | |
| // current instance is likely still usable. The caller should take the | |
| // failure as a cache miss or retry the same operation. | |
| // - `TransactionError::kConnectionError`: The connection to the backend was | |
| // lost (e.g., a lock could not be acquired). The caller should destroy the | |
| // instance and re-open with fresh parameters. | |
| // - `TransactionError::kPermanent`: A fatal, unrecoverable error occurred, | |
| // indicating data corruption. The caller should delete the backend storage | |
| // then destroy the instance. No new instance should be backed by the same | |
| // files before they are properly deleted and recreated. | |
| // | |
| // Resource Management: | |
| // A PersistentCache instance holds resources like open file handles for its | |
| // entire lifetime. It does not automatically release these on error. Destroying | |
| // the `PersistentCache` instance is required to release those resources. This | |
| // release then enables the caller to perform actions like deleting the cache | |
| // files if necessary/possible. | |
| // Use PersistentCache to store and retrieve key-value pairs across processes or | |
| // threads. | |
| class COMPONENT_EXPORT(PERSISTENT_CACHE) PersistentCache { | |
| public: | |
| // Returns a new instance on success or an error code on failure (e.g., if the | |
| // backend's files could not be opened or created, or the backend's storage is | |
| // corrupt). See class comments regarding error management. Unconditionally | |
| // consumes `pending_backend`. | |
| static base::expected<std::unique_ptr<PersistentCache>, TransactionError> | |
| Bind(Client client, PendingBackend pending_backend); | |
| explicit PersistentCache(Client client, std::unique_ptr<Backend> backend); | |
| ~PersistentCache(); | |
| // Not copyable or moveable. | |
| PersistentCache(const PersistentCache&) = delete; | |
| PersistentCache(PersistentCache&&) = delete; | |
| PersistentCache& operator=(const PersistentCache&) = delete; | |
| PersistentCache& operator=(PersistentCache&&) = delete; | |
| // Returns the metadata associated with `key` in the cache, or no value if not | |
| // found. If `key` is found, `buffer_provider` will be called exactly once | |
| // with the size of the content. If `buffer_provider` returns a non-empty span | |
| // (which must be sized exactly to the given content size), the content will | |
| // be written into it. If it returns an empty span, no data is copied. Note | |
| // that an error may be returned even if `buffer_provider` had been called. | |
| // See class comments regarding error management. | |
| // | |
| // Thread-safe. | |
| base::expected<std::optional<EntryMetadata>, TransactionError> Find( | |
| base::span<const uint8_t> key, | |
| BufferProvider buffer_provider); | |
| // Used to add an entry containing `content` and associated with `key`. | |
| // Metadata associated with the entry can be provided in `metadata` or the | |
| // object can be default initialized to signify no metadata. | |
| // Implementations are allowed to free other unused entries on demand to make | |
| // room or fail when full. Returns a empty value on success and error value | |
| // otherwise. See class comments regarding error management. | |
| // | |
| // Thread-safe. | |
| base::expected<void, TransactionError> Insert( | |
| base::span<const uint8_t> key, | |
| base::span<const uint8_t> content, | |
| EntryMetadata metadata = EntryMetadata{}); | |
| // Marks the instance as no longer suitable for use. Returns the state of the | |
| // shared lock at the moment of abandonment. Once an instance is abandoned, | |
| // all other instances that share a connection with it will report | |
| // `TransactionError::kConnectionError` for all operations. | |
| LockState Abandon(); | |
| Backend* GetBackendForTesting(); | |
| private: | |
| friend class BackendStorage; | |
| const Backend& backend() const { return *backend_; } | |
| std::optional<base::ElapsedTimer> MaybeGetTimerForHistogram(); | |
| const Client client_; | |
| std::unique_ptr<Backend> backend_; | |
| }; | |
| } // namespace persistent_cache | |
| #endif // COMPONENTS_PERSISTENT_CACHE_PERSISTENT_CACHE_H_ |