blob: 2a823c92a2c0ecdd87923eaac4ef9ae06054f6b8 [file] [edit]
// 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_DISK_CACHE_SQL_SQL_PERSISTENT_STORE_BACKEND_SHARD_H_
#define NET_DISK_CACHE_SQL_SQL_PERSISTENT_STORE_BACKEND_SHARD_H_
#include <atomic>
#include <optional>
#include "base/containers/flat_set.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/threading/sequence_bound.h"
#include "net/base/cache_type.h"
#include "net/base/io_buffer.h"
#include "net/disk_cache/sql/sql_persistent_store.h"
#include "net/disk_cache/sql/sql_persistent_store_in_memory_index.h"
#include "net/disk_cache/sql/sql_read_cache_memory_monitor.h"
namespace base {
class FilePath;
class SequencedTaskRunner;
} // namespace base
namespace net {
class IOBuffer;
} // namespace net
namespace disk_cache {
class EvictionCandidateAggregator;
// SqlPersistentStoreBackendShard` manages a single shard of the cache,
// including its own `Backend` instance and in-memory index. It forwards
// operations to the `Backend` on a dedicated background task runner.
class SqlPersistentStore::BackendShard {
public:
BackendShard(
ShardId shard_id,
const base::FilePath& path,
net::CacheType type,
scoped_refptr<SqlReadCacheMemoryMonitor> read_cache_memory_monitor,
scoped_refptr<base::SequencedTaskRunner> background_task_runner);
~BackendShard();
// Kicks off the asynchronous initialization of the backend.
void Initialize(int64_t user_max_bytes, InitResultOrErrorCallback callback);
void OpenOrCreateEntry(const CacheEntryKey& key,
EntryInfoOrErrorCallback callback);
void OpenEntry(const CacheEntryKey& key,
OptionalEntryInfoOrErrorCallback callback);
void CreateEntry(const CacheEntryKey& key,
base::Time creation_time,
EntryInfoOrErrorCallback callback);
void DoomEntry(const CacheEntryKey& key,
ResId res_id,
bool accept_index_mismatch,
ErrorCallback callback);
void DeleteDoomedEntry(const CacheEntryKey& key,
ResId res_id,
ErrorCallback callback);
void DeleteLiveEntry(const CacheEntryKey& key, ErrorCallback callback);
void DeleteAllEntries(ErrorCallback callback);
void DeleteLiveEntriesBetween(base::Time initial_time,
base::Time end_time,
base::flat_set<ResId> excluded_res_ids,
ErrorCallback callback);
void UpdateEntryLastUsedByKey(const CacheEntryKey& key,
base::Time last_used,
ErrorCallback callback);
void WriteEntryDataAndMetadata(
const CacheEntryKey& key,
std::optional<ResId> res_id,
std::optional<int64_t> old_body_end,
EntryWriteBuffer buffer,
base::Time last_used,
const std::optional<MemoryEntryDataHints>& new_hints,
scoped_refptr<net::IOBuffer> head_buffer,
int64_t header_size_delta,
bool doomed_new_entry,
ResIdOrErrorCallback callback);
void WriteEntryData(const CacheEntryKey& key,
const ResIdOrTime& res_id_or_last_used_time,
int64_t old_body_end,
EntryWriteBuffer buffer,
bool truncate,
bool doomed_new_entry,
ResIdOrErrorCallback callback);
void ReadEntryData(const CacheEntryKey& key,
ResId res_id,
int64_t offset,
scoped_refptr<net::IOBuffer> buffer,
int buf_len,
int64_t body_end,
bool sparse_reading,
SqlPersistentStore::ReadResultOrErrorCallback callback);
void GetEntryAvailableRange(const CacheEntryKey& key,
ResId res_id,
int64_t offset,
int len,
RangeResultCallback callback);
void CalculateSizeOfEntriesBetween(base::Time initial_time,
base::Time end_time,
Int64OrErrorCallback callback);
void OpenNextEntry(const EntryIterator& iterator,
OptionalEntryInfoWithKeyAndIteratorCallback callback);
void StartEviction(
int64_t size_to_be_removed,
base::flat_set<ResId> excluded_res_ids,
bool is_idle_time_eviction,
scoped_refptr<EvictionCandidateAggregator> aggregator,
scoped_refptr<base::RefCountedData<std::atomic_bool>> abort_flag,
scoped_refptr<base::RefCountedData<std::atomic_int64_t>>
remaining_mandatory_size,
ResIdListOrErrorCallback callback);
void ResumePendingEviction(
base::flat_set<ResId> excluded_res_ids,
bool is_idle_time_eviction,
scoped_refptr<base::RefCountedData<std::atomic_bool>> abort_flag,
scoped_refptr<base::RefCountedData<std::atomic_int64_t>>
remaining_mandatory_size,
ResIdListOrErrorCallback callback);
bool HasPendingEviction() const { return !pending_eviction_targets_.empty(); }
int32_t GetEntryCount() const;
void GetEntryCountAsync(Int32Callback callback) const;
int64_t GetSizeOfAllEntries() const;
IndexState GetIndexStateForHash(CacheEntryKey::Hash key_hash) const;
// Updates the in-memory index with the given hints for the specified entry.
void SetInMemoryEntryDataHints(ResId res_id, MemoryEntryDataHints hints);
// Retrieves the hints for the specified entry from the in-memory index, if
// available.
std::optional<MemoryEntryDataHints> GetInMemoryEntryDataHints(
CacheEntryKey::Hash key_hash) const;
// Tries to find a single resource ID for the given key hash in the in-memory
// index of this shard. Returns the resource ID if the index is available and
// contains a unique entry for the hash.
std::optional<ResId> TryGetSingleResIdFromInMemoryIndex(
CacheEntryKey::Hash key_hash) const;
void LoadInMemoryIndex(ErrorCallback callback);
// If there are entries that were doomed in a previous session, this method
// triggers a task to delete them from the database. The cleanup is performed
// in the background. Returns true if a cleanup task was scheduled, and false
// otherwise. `callback` is invoked upon completion of the cleanup task.
bool MaybeRunCleanupDoomedEntries(ErrorCallback callback);
void MaybeRunCheckpoint(base::OnceCallback<void(bool)> callback);
void EnableStrictCorruptionCheckForTesting();
void SetSimulateDbFailureForTesting(bool fail);
void RazeAndPoisonForTesting();
void SetEvictionHookForTesting(base::RepeatingClosure hook);
private:
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
// LINT.IfChange(IndexMismatchLocation)
enum class IndexMismatchLocation {
kOpenOrCreateEntry = 0,
kCreateEntry = 1,
kDoomEntry = 2,
kStartEviction = 3,
kDeleteLiveEntry = 4,
kDeleteLiveEntriesBetween = 5,
kWriteEntryDataAndMetadata = 6,
kWriteEntryData = 7,
kMaxValue = kWriteEntryData,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/net/enums.xml:SqlDiskCacheIndexMismatchLocation)
// Wraps a callback to ensure it is only run if the `BackendShard` is still
// alive.
template <typename ResultType>
base::OnceCallback<void(ResultType)> WrapCallback(
base::OnceCallback<void(ResultType)> callback) {
return base::BindOnce(
[](base::WeakPtr<BackendShard> weak_ptr,
base::OnceCallback<void(ResultType)> callback, ResultType result) {
if (weak_ptr) {
// We should not run the callback when `this` was deleted.
std::move(callback).Run(std::move(result));
}
},
weak_factory_.GetWeakPtr(), std::move(callback));
}
// Like `WrapCallback`, but also updates the `store_status_`.
base::OnceCallback<void(ErrorAndStoreStatus)> WrapCallbackWithStoreStatus(
ErrorCallback callback);
base::OnceCallback<void(ResIdOrErrorAndStoreStatus)>
WrapCallbackWithStoreStatusAndIndexUpdate(
ResIdOrErrorCallback callback,
const CacheEntryKey& key,
bool is_new_entry,
const std::optional<MemoryEntryDataHints>& new_hints,
IndexMismatchLocation location);
base::OnceCallback<void(EntryInfoOrErrorAndStoreStatus)>
WrapEntryInfoOrErrorCallback(EntryInfoOrErrorCallback callback,
const CacheEntryKey& key,
IndexMismatchLocation location);
base::OnceCallback<void(ResIdListOrErrorAndStoreStatus)>
WrapErrorCallbackToRemoveFromIndex(ErrorCallback callback,
IndexMismatchLocation location);
void OnEvictionFinished(ResIdListOrErrorCallback callback,
EvictionResultOrErrorAndStoreStatus result);
void RecordIndexMismatch(IndexMismatchLocation location);
base::SequenceBound<Backend> backend_;
// The in-memory summary of the store's status.
StoreStatus store_status_;
// The in-memory index of cache entries. This is loaded asynchronously after
// MaybeLoadInMemoryIndex() is called.
std::optional<SqlPersistentStoreInMemoryIndex> index_;
// A list of resource IDs for entries that were doomed in a previous session
// and are scheduled for deletion.
ResIdList to_be_deleted_res_ids_;
// True while the in-memory index is being loaded from the database.
bool loading_index_ = false;
// A list of resource IDs of entries that are doomed during the in-memory
// index is being loaded. Once loading is complete, these entries are removed
// from the newly loaded index to ensure consistency.
ResIdList pending_doomed_res_ids_;
bool strict_corruption_check_enabled_ = false;
// The list of eviction candidates that were selected but not yet evicted
// because the eviction was paused.
SqlPersistentStore::EvictionTargetQueue pending_eviction_targets_;
base::WeakPtrFactory<BackendShard> weak_factory_{this};
};
} // namespace disk_cache
#endif // NET_DISK_CACHE_SQL_SQL_PERSISTENT_STORE_BACKEND_SHARD_H_