| // Copyright 2026 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_DISK_CACHE_SQL_SQL_SHARED_CACHE_H_ |
| #define NET_DISK_CACHE_SQL_SQL_SHARED_CACHE_H_ |
| |
| #include <atomic> |
| #include <vector> |
| |
| #include "base/containers/id_map.h" |
| #include "base/containers/queue.h" |
| #include "base/functional/callback.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/raw_ref.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/threading/sequence_bound.h" |
| #include "base/types/pass_key.h" |
| #include "net/base/net_export.h" |
| #include "net/base/network_isolation_key.h" |
| #include "net/disk_cache/sql/cache_entry_key.h" |
| #include "net/disk_cache/sql/shared_cache_client_remote.h" |
| #include "net/disk_cache/sql/sql_persistent_store.h" |
| #include "net/disk_cache/sql/sql_read_cache_memory_monitor.h" |
| #include "net/disk_cache/sql/sql_shared_cache_blob_handle.h" |
| #include "net/disk_cache/sql/sql_shared_cache_isolated_database.h" |
| #include "net/disk_cache/sql/sql_tracked_sequence_bound.h" |
| #include "third_party/abseil-cpp/absl/container/flat_hash_set.h" |
| |
| namespace disk_cache { |
| |
| class BackendCleanupTracker; |
| class SqlPersistentStore; |
| class SqlSharedCacheHandle; |
| |
| // Represents an isolated SQL disk cache instance shared across requests |
| // matching a specific NetworkIsolationKey (or string representation). |
| // |
| // Managed by `SqlSharedCacheManager`, and reference-counted via |
| // `SqlSharedCacheHandle`. When all handles (`SqlSharedCacheHandle`) referencing |
| // this cache are destroyed, `SqlSharedCacheManager` cleans up and deletes this |
| // cache object. |
| class NET_EXPORT_PRIVATE SqlSharedCache { |
| public: |
| SqlSharedCache( |
| std::string nik_string, |
| SqlPersistentStore& store, |
| const base::FilePath& directory, |
| base::RepeatingCallback<void(SqlSharedCache&)> on_unreferenced_callback, |
| scoped_refptr<base::SequencedTaskRunner> db_task_runner, |
| scoped_refptr<SqlReadCacheMemoryMonitor> read_cache_memory_monitor, |
| scoped_refptr<BackendCleanupTracker> cleanup_tracker); |
| ~SqlSharedCache(); |
| |
| SqlSharedCache(const SqlSharedCache&) = delete; |
| SqlSharedCache& operator=(const SqlSharedCache&) = delete; |
| |
| // Asynchronously cleans up resources and notifies `callback` when complete. |
| void Cleanup(base::OnceClosure callback); |
| |
| // Initializes the underlying isolated database instance for this shared cache |
| // associated with `shared_cache_db_id`. |
| void InitIsolatedDatabase(SqlSharedCacheDbId shared_cache_db_id, |
| base::OnceCallback<void(bool)> callback); |
| |
| // Creates a new reference-counted `SqlSharedCacheHandle` targeting this |
| // cache. |
| scoped_refptr<SqlSharedCacheHandle> CreateHandle(); |
| |
| // Returns true if there are any active handles referencing this cache. |
| bool IsReferenced() const { return handle_count_ != 0; } |
| size_t handle_count_for_testing() const { return handle_count_; } |
| |
| // Increments/decrements the count of active `SqlSharedCacheHandle` instances. |
| // Restricted via `base::PassKey` to `SqlSharedCacheHandle`. |
| void IncrementHandleCount(base::PassKey<SqlSharedCacheHandle>); |
| void DecrementHandleCount(base::PassKey<SqlSharedCacheHandle>); |
| |
| // Returns the string key derived from NetworkIsolationKey identifying this |
| // cache. |
| const std::string& nik_string() const { return nik_string_; } |
| |
| // Returns the database ID associated with this shared cache, if initialized. |
| std::optional<SqlSharedCacheDbId> shared_cache_db_id() const { |
| return shared_cache_db_id_; |
| } |
| |
| // Returns the task runner used for DB operations. |
| scoped_refptr<base::SequencedTaskRunner> db_task_runner() const { |
| return db_task_runner_; |
| } |
| |
| SqlTrackedSequenceBound<SqlSharedCacheIsolatedDatabase>& |
| isolated_database_for_testing() { |
| return isolated_database_; |
| } |
| |
| // Copies multiple shared-cache eligible entries into the shared cache. Must |
| // only be called when `entries` is non-empty, no copy operation is currently |
| // in progress, and `InitIsolatedDatabase` has completed (`shared_cache_db_id` |
| // and `isolated_database_` are set). |
| void CopyEntries( |
| base::queue<SqlPersistentStore::SharedCacheEligibleEntry> entries, |
| scoped_refptr<base::RefCountedData<std::atomic_bool>> abort_flag, |
| base::OnceCallback<void( |
| base::queue<SqlPersistentStore::SharedCacheEligibleEntry>)> callback, |
| base::RepeatingCallback<void(const CacheEntryKey&)> |
| on_entry_copied_callback = {}); |
| |
| // Deletes entries specified by `shared_cache_row_ids` from the isolated |
| // database. |
| void DeleteEntries( |
| const std::vector<SqlSharedCacheRowId>& shared_cache_row_ids, |
| base::OnceCallback< |
| void(base::expected<void, SqlSharedCacheIsolatedDatabase::Error>)> |
| callback); |
| |
| // Asynchronously reads entry body data from the shared cache isolated |
| // database into `buffer` starting at `offset`. `body_size` is the total body |
| // size used to validate the read range. |
| void Read(const CacheEntryKey& entry_key, |
| SqlSharedCacheRowId shared_cache_row_id, |
| int body_size, |
| int64_t offset, |
| scoped_refptr<net::IOBuffer> buffer, |
| SqlPersistentStore::ReadResultOrErrorCallback callback); |
| |
| // Asynchronously retrieves a `SqlSharedCacheBlobHandle` for a shared cache |
| // entry. |
| void GetBlobHandle( |
| const CacheEntryKey& entry_key, |
| SqlSharedCacheRowId shared_cache_row_id, |
| int body_size, |
| base::OnceCallback<void( |
| base::expected<scoped_refptr<SqlSharedCacheBlobHandle>, |
| SqlSharedCacheIsolatedDatabase::Error>)> callback); |
| |
| // Registers a remote client to receive database connection handles. |
| void RegisterClient(std::unique_ptr<SharedCacheClientRemote> client); |
| |
| private: |
| using ClientId = int32_t; |
| using ClientsMap = |
| base::IDMap<std::unique_ptr<SharedCacheClientRemote>, ClientId>; |
| |
| // Entry Copying Call Flow Overview: |
| // |
| // CopyEntries() |
| // | |
| // v (empty/abort) |
| // CopyNextEntry() --------------> FinishCopy() |
| // ^ | ^ |
| // | | +------------------------------------------+ |
| // | | | |
| // | v (error) | |
| // | OnEntryOpenedForSharedCache() ----------------> OnCopyEntryFailed() |
| // | | (body == 0) | (body > 0) ^ ^ ^ ^ ^ |
| // | | v (error) | | | | | |
| // | | OnEntryDataReadForInsert() ----------+ | | | | |
| // | | | (OK) | | | | |
| // | v v (error) | | | | |
| // | OnIsolatedDatabaseInserted() -----------------------+ | | | |
| // | | (OK) | | | |
| // | v | | | |
| // | ReadNextChunk() | | | |
| // | |(done) ^ | (has data) | | | |
| // | | | v (error) | | | |
| // | | | OnEntryDataRead() ----------+ | | |
| // | | | | (OK) | | |
| // | | (OK)| v (error)| | |
| // | | +- OnIsolatedDatabaseWritten() -------+ | |
| // | | | |
| // | v (error) | |
| // | MoveBlobsToSharedCache() ---------------------------------+ |
| // | | (OK) |
| // | v |
| // +----- OnCopyEntryComplete() |
| void CopyNextEntry(); |
| void OnEntryOpenedForSharedCache( |
| SqlPersistentStore::SharedCacheEligibleEntry entry, |
| SqlPersistentStore::EntryInfoOrError result); |
| void OnEntryDataReadForInsert( |
| SqlPersistentStore::SharedCacheEligibleEntry entry, |
| SqlPersistentStore::ResId res_id, |
| scoped_refptr<net::PickledIOBuffer> headers, |
| int64_t body_end, |
| scoped_refptr<net::IOBuffer> buffer, |
| base::expected<SqlPersistentStore::ReadResult, SqlPersistentStore::Error> |
| result); |
| void OnIsolatedDatabaseInserted( |
| CacheEntryKey key, |
| SqlPersistentStore::ResId res_id, |
| int64_t body_end, |
| int64_t offset, |
| base::expected<SqlSharedCacheRowId, SqlSharedCacheIsolatedDatabase::Error> |
| result); |
| void ReadNextChunk(CacheEntryKey key, |
| SqlPersistentStore::ResId res_id, |
| int64_t body_end, |
| int64_t offset, |
| SqlSharedCacheRowId shared_cache_row_id); |
| void OnEntryDataRead(CacheEntryKey key, |
| SqlPersistentStore::ResId res_id, |
| int64_t body_end, |
| int64_t offset, |
| SqlSharedCacheRowId shared_cache_row_id, |
| scoped_refptr<net::IOBuffer> buffer, |
| base::expected<SqlPersistentStore::ReadResult, |
| SqlPersistentStore::Error> result); |
| void OnIsolatedDatabaseWritten( |
| CacheEntryKey key, |
| SqlPersistentStore::ResId res_id, |
| int64_t body_end, |
| int64_t next_offset, |
| SqlSharedCacheRowId shared_cache_row_id, |
| base::expected<void, SqlSharedCacheIsolatedDatabase::Error> result); |
| void MoveBlobsToSharedCache(CacheEntryKey key, |
| SqlPersistentStore::ResId res_id, |
| SqlSharedCacheRowId shared_cache_row_id); |
| void OnCopyEntryComplete(const CacheEntryKey& key); |
| void OnCopyEntryFailed(); |
| void FinishCopy(); |
| |
| void OnClientDisconnected(ClientId client_id, |
| scoped_refptr<SqlSharedCacheHandle> handle); |
| |
| void OnIsolatedDatabaseRead( |
| SqlPersistentStore::ReadResultOrErrorCallback callback, |
| SqlSharedCacheIsolatedDatabase::ReadResultOrError result); |
| |
| void OnPendingFileSetForClient( |
| ClientId client_id, |
| base::expected<sqlite_vfs::PendingFileSet, |
| SqlSharedCacheIsolatedDatabase::Error> pending_file_set); |
| |
| void OnHashesLoaded( |
| base::expected<std::vector<uint32_t>, |
| SqlSharedCacheIsolatedDatabase::Error> hashes); |
| |
| const std::string nik_string_; |
| const raw_ref<SqlPersistentStore> store_; |
| const base::FilePath directory_; |
| |
| base::RepeatingCallback<void(SqlSharedCache&)> on_unreferenced_callback_; |
| int handle_count_ = 0; |
| scoped_refptr<base::SequencedTaskRunner> db_task_runner_; |
| scoped_refptr<SqlReadCacheMemoryMonitor> read_cache_memory_monitor_; |
| scoped_refptr<BackendCleanupTracker> cleanup_tracker_; |
| |
| std::optional<SqlSharedCacheDbId> shared_cache_db_id_; |
| |
| SqlTrackedSequenceBound<SqlSharedCacheIsolatedDatabase> isolated_database_; |
| |
| std::optional<absl::flat_hash_set<uint32_t>> cached_hashes_; |
| ClientsMap clients_; |
| |
| base::queue<SqlPersistentStore::SharedCacheEligibleEntry> |
| pending_copy_entries_; |
| scoped_refptr<base::RefCountedData<std::atomic_bool>> copy_abort_flag_; |
| base::OnceCallback<void( |
| base::queue<SqlPersistentStore::SharedCacheEligibleEntry>)> |
| copy_callback_; |
| std::optional<SqlSharedCacheRowId> current_copy_row_id_; |
| |
| base::RepeatingCallback<void(const CacheEntryKey&)> on_entry_copied_callback_; |
| std::optional<uint32_t> current_entry_hash_; |
| std::vector<uint32_t> copy_new_hashes_; |
| |
| base::WeakPtrFactory<SqlSharedCache> weak_factory_{this}; |
| }; |
| |
| } // namespace disk_cache |
| |
| #endif // NET_DISK_CACHE_SQL_SQL_SHARED_CACHE_H_ |