| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef NET_HTTP_NO_VARY_SEARCH_CACHE_STORAGE_H_ |
| #define NET_HTTP_NO_VARY_SEARCH_CACHE_STORAGE_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| |
| #include "base/functional/callback_forward.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/time/time.h" |
| #include "base/types/expected.h" |
| #include "net/base/net_export.h" |
| #include "net/http/no_vary_search_cache.h" |
| |
| namespace net { |
| |
| class NoVarySearchCache; |
| class NoVarySearchCacheStorageFileOperations; |
| |
| // An implementation of persistence for NoVarySearchCache. Disk operations are |
| // performed in the background on a thread pool. |
| class NET_EXPORT_PRIVATE NoVarySearchCacheStorage final |
| : public NoVarySearchCache::Journal { |
| public: |
| enum class LoadFailed { kCannotJournal }; |
| |
| using LoadResult = |
| base::expected<std::unique_ptr<NoVarySearchCache>, LoadFailed>; |
| |
| using LoadCallback = base::OnceCallback<void(LoadResult)>; |
| |
| // Exposed for testing. |
| static constexpr std::string_view kSnapshotFilename = "snapshot.baf"; |
| static constexpr std::string_view kJournalFilename = "journal.baj"; |
| |
| // Generated by the command: |
| // echo "snapshot.baf version 2" | md5sum | cut -b 1-8 |
| static constexpr uint32_t kSnapshotMagicNumber = 0x62f965ce; |
| |
| // Generated by the command: |
| // echo "journal.baj version 2" | md5sum | cut -b 1-8 |
| static constexpr uint32_t kJournalMagicNumber = 0x24467e06; |
| |
| // Max file size. We won't try to load a file larger than this. We don't |
| // expect the database to ever get this big. |
| static constexpr size_t kMaxFileSize = 100 * 1024 * 1024; |
| |
| // Avoid automatically taking a new snapshot until the journal gets this big, |
| // even if the snapshot is smaller. |
| static constexpr size_t kMinimumAutoSnapshotSize = 64 * 1024; |
| |
| // Create a NoVarySearchCacheStorage object to persist `cache`. Load() must be |
| // called in order for it to actually do anything. |
| NoVarySearchCacheStorage(); |
| |
| NoVarySearchCacheStorage(const NoVarySearchCacheStorage&) = delete; |
| NoVarySearchCacheStorage& operator=(const NoVarySearchCacheStorage&) = delete; |
| |
| ~NoVarySearchCacheStorage() override; |
| |
| // Asynchronously loads a previously persisted NoVarySearchCache object using |
| // `file_operations`, then starts journalling to a new journal file. If an |
| // existing persisted cache could not be loaded, creates an empty |
| // NoVarySearchCache object with `max_size` configured to be |
| // `default_max_size`. If a new journal file could not be created, `callback` |
| // is called with base::unexpected(kCannotJournal). Otherwise it is called |
| // with a NoVarySearchCache object wrapped in a std::unique_ptr. `callback` |
| // should merge in any entries that were added to the temporary cache used |
| // during loading, and then switch to using the new cache. Any inserts or |
| // erasures performed on the new cache will be journalled to disk. The new |
| // NoVarySearchCache object should not be destroyed before this object is. |
| // `callback` is never called synchronously. |
| void Load( |
| std::unique_ptr<NoVarySearchCacheStorageFileOperations> file_operations, |
| size_t default_max_size, |
| LoadCallback callback); |
| |
| // Serializes and persists the current state of `cache_` to disk, and starts a |
| // new empty journal. Does nothing if the initial load hasn't completed yet. |
| // This method is also called automatically any time the journal file grows |
| // too large. |
| void TakeSnapshot(); |
| |
| // Implementation of NoVarySearchCache::Journal. |
| |
| // Serializes the arguments and posts a task to `journal_` to append the |
| // result to the journal. |
| void OnInsert(const std::string& partition_key, |
| const std::string& base_url, |
| const HttpNoVarySearchData& nvs_data, |
| const std::optional<std::string>& query, |
| base::Time update_time) override; |
| |
| // Serializes the arguments and posts a task to `journal_` to append to the |
| // journal. |
| void OnErase(const std::string& partition_key, |
| const std::string& base_url, |
| const HttpNoVarySearchData& nvs_data, |
| const std::optional<std::string>& query) override; |
| |
| bool IsJournallingForTesting() const { return static_cast<bool>(journal_); } |
| |
| private: |
| class Journaller; |
| class Loader; |
| |
| using JournallerPtr = std::unique_ptr<Journaller, base::OnTaskRunnerDeleter>; |
| |
| // On successful creation of the Journaller on the background thread, a |
| // pointer to it needs to be passed back to the |
| // NoVarySearchCacheStorage::OnLoadComplete() method so that it can be called |
| // and have its lifetime managed. We also need the loaded NoVarySearchCache |
| // object. |
| struct CacheAndJournalPointers { |
| CacheAndJournalPointers(std::unique_ptr<NoVarySearchCache> cache, |
| JournallerPtr journal); |
| |
| CacheAndJournalPointers(const CacheAndJournalPointers&) = delete; |
| |
| // Move construction is allowed. |
| CacheAndJournalPointers(CacheAndJournalPointers&&); |
| |
| CacheAndJournalPointers& operator=(const CacheAndJournalPointers&) = delete; |
| CacheAndJournalPointers& operator=(CacheAndJournalPointers&&) = delete; |
| |
| ~CacheAndJournalPointers(); |
| |
| std::unique_ptr<NoVarySearchCache> cache; |
| JournallerPtr journal; |
| }; |
| |
| // Called when loading the persisted NoVarySearchCache has succeeded or |
| // failed. Sets `cache_`, registers this object as a Journal for it, and |
| // assigns to `journal_` on success. Calls `callback`. |
| void OnLoadComplete( |
| LoadCallback callback, |
| base::expected<CacheAndJournalPointers, LoadFailed> result); |
| |
| // Called when `journal_` has found that journalling failed. Clears `journal_` |
| // and unregisters this object as an observer. |
| void OnJournallingFailed(); |
| |
| // Appends `pickle` to the journal file. |
| void AppendToJournal(base::Pickle pickle); |
| |
| raw_ptr<NoVarySearchCache> cache_ = nullptr; |
| |
| // Task runner for file operations. |
| scoped_refptr<base::SequencedTaskRunner> background_task_runner_; |
| |
| // `journal_` lives on the `background_task_runner_`. |
| JournallerPtr journal_; |
| |
| // Time when Start() was called. |
| base::Time start_time_; |
| |
| base::WeakPtrFactory<NoVarySearchCacheStorage> weak_factory_{this}; |
| }; |
| |
| } // namespace net |
| |
| #endif // NET_HTTP_NO_VARY_SEARCH_CACHE_STORAGE_H_ |