blob: bb1e3b510fb0b404b1e60d964fe20e76d67289b0 [file]
// 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_