| // 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. |
| |
| #include "net/disk_cache/sql/sql_persistent_store_backend_shard.h" |
| |
| #include <string_view> |
| #include <utility> |
| |
| #include "base/check.h" |
| #include "base/functional/bind.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/strings/strcat.h" |
| #include "base/task/bind_post_task.h" |
| #include "base/time/time.h" |
| #include "base/types/expected.h" |
| #include "net/base/features.h" |
| #include "net/disk_cache/memory_entry_data_hints.h" |
| #include "net/disk_cache/sql/eviction_candidate_aggregator.h" |
| #include "net/disk_cache/sql/sql_backend_constants.h" |
| #include "net/disk_cache/sql/sql_persistent_store_backend.h" |
| |
| namespace disk_cache { |
| |
| SqlPersistentStore::BackendShard::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) |
| : backend_(background_task_runner, |
| shard_id, |
| path, |
| type, |
| std::move(read_cache_memory_monitor)) {} |
| |
| SqlPersistentStore::BackendShard::~BackendShard() = default; |
| |
| // Kicks off the asynchronous initialization of the backend. |
| void SqlPersistentStore::BackendShard::Initialize( |
| int64_t user_max_bytes, |
| InitResultOrErrorCallback callback) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::Initialize) |
| .WithArgs(user_max_bytes, base::TimeTicks::Now()) |
| .Then(base::BindOnce( |
| [](base::WeakPtr<BackendShard> weak_ptr, |
| InitResultOrErrorCallback callback, InitResultOrError result) { |
| if (weak_ptr) { |
| if (result.has_value()) { |
| weak_ptr->store_status_ = result->store_status; |
| if (result->in_memory_data) { |
| weak_ptr->index_ = std::move(result->in_memory_data->index); |
| weak_ptr->to_be_deleted_res_ids_ = |
| std::move(result->in_memory_data->doomed_entry_res_ids); |
| } |
| } |
| std::move(callback).Run(std::move(result)); |
| } |
| }, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void SqlPersistentStore::BackendShard::OpenOrCreateEntry( |
| const CacheEntryKey& key, |
| EntryInfoOrErrorCallback callback) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::OpenOrCreateEntry) |
| .WithArgs(key, base::TimeTicks::Now()) |
| .Then(WrapEntryInfoOrErrorCallback( |
| std::move(callback), key, IndexMismatchLocation::kOpenOrCreateEntry)); |
| } |
| |
| void SqlPersistentStore::BackendShard::OpenEntry( |
| const CacheEntryKey& key, |
| OptionalEntryInfoOrErrorCallback callback) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::OpenEntry) |
| .WithArgs(key, base::TimeTicks::Now()) |
| .Then(WrapCallback(std::move(callback))); |
| } |
| |
| void SqlPersistentStore::BackendShard::CreateEntry( |
| const CacheEntryKey& key, |
| base::Time creation_time, |
| EntryInfoOrErrorCallback callback) { |
| bool run_existance_check = !index_ || index_->Contains(key.hash()); |
| backend_.AsyncCall(&SqlPersistentStore::Backend::CreateEntry) |
| .WithArgs(key, creation_time, run_existance_check, base::TimeTicks::Now()) |
| .Then(WrapEntryInfoOrErrorCallback(std::move(callback), key, |
| IndexMismatchLocation::kCreateEntry)); |
| } |
| |
| void SqlPersistentStore::BackendShard::DoomEntry(const CacheEntryKey& key, |
| ResId res_id, |
| bool accept_index_mismatch, |
| ErrorCallback callback) { |
| bool need_recovery_on_failure = false; |
| if (index_.has_value()) { |
| // If the in-memory index is available, synchronously remove the entry from |
| // the index. |
| if (index_->Remove(key.hash(), res_id)) { |
| need_recovery_on_failure = true; |
| } else if (!accept_index_mismatch) { |
| RecordIndexMismatch(IndexMismatchLocation::kDoomEntry); |
| } |
| } else if (loading_index_) { |
| // If the in-memory index is being loaded, add to `pending_doomed_res_ids_` |
| // to be removed from the index upon completion of the index loading. |
| pending_doomed_res_ids_.emplace_back(res_id); |
| } |
| backend_.AsyncCall(&SqlPersistentStore::Backend::DoomEntry) |
| .WithArgs(key, res_id, base::TimeTicks::Now()) |
| .Then(base::BindOnce( |
| [](base::WeakPtr<BackendShard> weak_ptr, |
| bool need_recovery_on_failure, CacheEntryKey::Hash hash, |
| ResId res_id, ErrorCallback callback, ErrorAndStoreStatus result) { |
| if (weak_ptr) { |
| // If the DoomEntry operation fails in the database, the entry |
| // needs to be re-inserted into the in-memory index to maintain |
| // consistency. |
| if (need_recovery_on_failure && result.result != Error::kOk && |
| result.result != Error::kNotFound) { |
| weak_ptr->index_->Insert(hash, res_id); |
| } |
| weak_ptr->store_status_ = result.store_status; |
| // We should not run the callback when `this` was deleted. |
| std::move(callback).Run(std::move(result.result)); |
| } |
| }, |
| weak_factory_.GetWeakPtr(), need_recovery_on_failure, key.hash(), |
| res_id, std::move(callback))); |
| } |
| |
| void SqlPersistentStore::BackendShard::DeleteDoomedEntry( |
| const CacheEntryKey& key, |
| ResId res_id, |
| ErrorCallback callback) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::DeleteDoomedEntry) |
| .WithArgs(key, res_id, base::TimeTicks::Now()) |
| .Then(WrapCallbackWithStoreStatus(std::move(callback))); |
| } |
| |
| void SqlPersistentStore::BackendShard::DeleteLiveEntry(const CacheEntryKey& key, |
| ErrorCallback callback) { |
| // If the entry is not in the in-memory index, we can skip the DB lookup. |
| if (GetIndexStateForHash(key.hash()) == IndexState::kHashNotFound) { |
| std::move(callback).Run(Error::kNotFound); |
| return; |
| } |
| backend_.AsyncCall(&SqlPersistentStore::Backend::DeleteLiveEntry) |
| .WithArgs(key, base::TimeTicks::Now()) |
| .Then(WrapErrorCallbackToRemoveFromIndex( |
| std::move(callback), IndexMismatchLocation::kDeleteLiveEntry)); |
| } |
| |
| void SqlPersistentStore::BackendShard::DeleteAllEntries( |
| ErrorCallback callback) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::DeleteAllEntries) |
| .WithArgs(base::TimeTicks::Now()) |
| .Then(base::BindOnce( |
| [](base::WeakPtr<BackendShard> weak_ptr, ErrorCallback callback, |
| ErrorAndStoreStatus result) { |
| if (weak_ptr) { |
| if (result.result == Error::kOk && weak_ptr->index_.has_value()) { |
| weak_ptr->index_->Clear(); |
| } |
| weak_ptr->store_status_ = result.store_status; |
| // We should not run the callback when `this` was deleted. |
| std::move(callback).Run(std::move(result.result)); |
| } |
| }, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void SqlPersistentStore::BackendShard::DeleteLiveEntriesBetween( |
| base::Time initial_time, |
| base::Time end_time, |
| base::flat_set<ResId> excluded_res_ids, |
| ErrorCallback callback) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::DeleteLiveEntriesBetween) |
| .WithArgs(initial_time, end_time, std::move(excluded_res_ids), |
| base::TimeTicks::Now()) |
| .Then(WrapErrorCallbackToRemoveFromIndex( |
| std::move(callback), |
| IndexMismatchLocation::kDeleteLiveEntriesBetween)); |
| } |
| |
| void SqlPersistentStore::BackendShard::UpdateEntryLastUsedByKey( |
| const CacheEntryKey& key, |
| base::Time last_used, |
| ErrorCallback callback) { |
| // If the entry is not in the in-memory index, we can skip the DB lookup. |
| if (GetIndexStateForHash(key.hash()) == IndexState::kHashNotFound) { |
| std::move(callback).Run(Error::kNotFound); |
| return; |
| } |
| backend_.AsyncCall(&SqlPersistentStore::Backend::UpdateEntryLastUsedByKey) |
| .WithArgs(key, last_used, base::TimeTicks::Now()) |
| .Then(WrapCallback(std::move(callback))); |
| } |
| |
| void SqlPersistentStore::BackendShard::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) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::WriteEntryDataAndMetadata) |
| .WithArgs(key, res_id, old_body_end, std::move(buffer), last_used, |
| new_hints, std::move(head_buffer), header_size_delta, |
| doomed_new_entry, base::TimeTicks::Now()) |
| .Then(WrapCallbackWithStoreStatusAndIndexUpdate( |
| std::move(callback), key, |
| /*is_new_entry=*/!res_id.has_value(), new_hints, |
| IndexMismatchLocation::kWriteEntryDataAndMetadata)); |
| } |
| |
| void SqlPersistentStore::BackendShard::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) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::WriteEntryData) |
| .WithArgs(key, res_id_or_last_used_time, old_body_end, std::move(buffer), |
| truncate, doomed_new_entry, base::TimeTicks::Now()) |
| .Then(WrapCallbackWithStoreStatusAndIndexUpdate( |
| std::move(callback), key, |
| /*is_new_entry=*/ |
| std::holds_alternative<base::Time>(res_id_or_last_used_time), |
| /*new_hints=*/std::nullopt, IndexMismatchLocation::kWriteEntryData)); |
| } |
| |
| void SqlPersistentStore::BackendShard::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) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::ReadEntryData) |
| .WithArgs(key, res_id, offset, std::move(buffer), buf_len, body_end, |
| sparse_reading, base::TimeTicks::Now()) |
| .Then(WrapCallback(std::move(callback))); |
| } |
| |
| void SqlPersistentStore::BackendShard::GetEntryAvailableRange( |
| const CacheEntryKey& key, |
| ResId res_id, |
| int64_t offset, |
| int len, |
| RangeResultCallback callback) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::GetEntryAvailableRange) |
| .WithArgs(res_id, offset, len, base::TimeTicks::Now()) |
| .Then(WrapCallback(std::move(callback))); |
| } |
| |
| void SqlPersistentStore::BackendShard::CalculateSizeOfEntriesBetween( |
| base::Time initial_time, |
| base::Time end_time, |
| Int64OrErrorCallback callback) { |
| backend_ |
| .AsyncCall(&SqlPersistentStore::Backend::CalculateSizeOfEntriesBetween) |
| .WithArgs(initial_time, end_time, base::TimeTicks::Now()) |
| .Then(WrapCallback(std::move(callback))); |
| } |
| |
| void SqlPersistentStore::BackendShard::OpenNextEntry( |
| const EntryIterator& iterator, |
| OptionalEntryInfoWithKeyAndIteratorCallback callback) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::OpenNextEntry) |
| .WithArgs(iterator, base::TimeTicks::Now()) |
| .Then(WrapCallback(std::move(callback))); |
| } |
| |
| void SqlPersistentStore::BackendShard::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) { |
| EvictionResultOrErrorAndStoreStatusCallback result_callback = |
| base::BindPostTaskToCurrentDefault( |
| base::BindOnce(&BackendShard::OnEvictionFinished, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| |
| std::vector<ResId> high_priority_res_ids; |
| if (net::features::kSqlDiskCacheSizeAndPriorityAwareEviction.Get()) { |
| CHECK(index_); |
| // Retrieve the list of high priority resource IDs from the in-memory index |
| // to pass to the backend for prioritized eviction. |
| high_priority_res_ids = |
| index_->GetResIdsWithHints(MemoryEntryDataHints(HINT_HIGH_PRIORITY)); |
| } |
| backend_.AsyncCall(&SqlPersistentStore::Backend::StartEviction) |
| .WithArgs(size_to_be_removed, std::move(excluded_res_ids), |
| std::move(high_priority_res_ids), is_idle_time_eviction, |
| std::move(aggregator), std::move(abort_flag), |
| std::move(remaining_mandatory_size), |
| std::move(result_callback)); |
| } |
| |
| void SqlPersistentStore::BackendShard::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) { |
| if (pending_eviction_targets_.empty()) { |
| std::move(callback).Run(ResIdList()); |
| return; |
| } |
| backend_.AsyncCall(&SqlPersistentStore::Backend::ResumePendingEviction) |
| .WithArgs(std::move(pending_eviction_targets_), |
| std::move(excluded_res_ids), is_idle_time_eviction, |
| std::move(abort_flag), std::move(remaining_mandatory_size), |
| base::TimeTicks::Now()) |
| .Then(base::BindOnce(&BackendShard::OnEvictionFinished, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| int32_t SqlPersistentStore::BackendShard::GetEntryCount() const { |
| return store_status_.entry_count; |
| } |
| |
| void SqlPersistentStore::BackendShard::GetEntryCountAsync( |
| Int32Callback callback) const { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::GetEntryCount) |
| .Then(std::move(callback)); |
| } |
| |
| int64_t SqlPersistentStore::BackendShard::GetSizeOfAllEntries() const { |
| return store_status_.GetEstimatedDiskUsage(); |
| } |
| |
| void SqlPersistentStore::BackendShard::LoadInMemoryIndex( |
| ErrorCallback callback) { |
| CHECK(!loading_index_); |
| CHECK(!index_.has_value()); |
| loading_index_ = true; |
| backend_.AsyncCall(&SqlPersistentStore::Backend::LoadInMemoryIndex) |
| .Then(base::BindOnce( |
| [](base::WeakPtr<BackendShard> weak_ptr, ErrorCallback callback, |
| SqlPersistentStore::InMemoryIndexAndDoomedResIdsOrError result) { |
| if (weak_ptr) { |
| if (result.has_value()) { |
| weak_ptr->index_ = std::move(result->index); |
| weak_ptr->to_be_deleted_res_ids_ = |
| std::move(result->doomed_entry_res_ids); |
| weak_ptr->loading_index_ = false; |
| for (auto doomed_res_id : weak_ptr->pending_doomed_res_ids_) { |
| weak_ptr->index_->Remove(doomed_res_id); |
| } |
| weak_ptr->pending_doomed_res_ids_.clear(); |
| } |
| std::move(callback).Run(result.has_value() ? Error::kOk |
| : result.error()); |
| } |
| }, |
| weak_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| bool SqlPersistentStore::BackendShard::MaybeRunCleanupDoomedEntries( |
| ErrorCallback callback) { |
| if (to_be_deleted_res_ids_.empty()) { |
| return false; |
| } |
| backend_.AsyncCall(&SqlPersistentStore::Backend::DeleteDoomedEntries) |
| .WithArgs(std::move(to_be_deleted_res_ids_), base::TimeTicks::Now()) |
| .Then(WrapCallback(std::move(callback))); |
| return true; |
| } |
| |
| void SqlPersistentStore::BackendShard::MaybeRunCheckpoint( |
| base::OnceCallback<void(bool)> callback) { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::MaybeRunCheckpoint) |
| .Then(std::move(callback)); |
| } |
| |
| void SqlPersistentStore::BackendShard::EnableStrictCorruptionCheckForTesting() { |
| strict_corruption_check_enabled_ = true; |
| backend_.AsyncCall( |
| &SqlPersistentStore::Backend::EnableStrictCorruptionCheckForTesting); |
| } |
| |
| void SqlPersistentStore::BackendShard::SetSimulateDbFailureForTesting( |
| bool fail) { |
| backend_ |
| .AsyncCall(&SqlPersistentStore::Backend::SetSimulateDbFailureForTesting) |
| .WithArgs(fail); |
| } |
| |
| void SqlPersistentStore::BackendShard::RazeAndPoisonForTesting() { |
| backend_.AsyncCall(&SqlPersistentStore::Backend::RazeAndPoisonForTesting); |
| } |
| |
| void SqlPersistentStore::BackendShard::SetEvictionHookForTesting( // IN-TEST |
| base::RepeatingClosure hook) { |
| backend_.AsyncCall(&Backend::SetEvictionHookForTesting) |
| .WithArgs(std::move(hook)); |
| } |
| |
| SqlPersistentStore::IndexState |
| SqlPersistentStore::BackendShard::GetIndexStateForHash( |
| CacheEntryKey::Hash key_hash) const { |
| if (!index_.has_value()) { |
| return IndexState::kNotReady; |
| } |
| |
| if (index_->Contains(key_hash)) { |
| return IndexState::kHashFound; |
| } |
| return IndexState::kHashNotFound; |
| } |
| |
| void SqlPersistentStore::BackendShard::SetInMemoryEntryDataHints( |
| ResId res_id, |
| MemoryEntryDataHints hints) { |
| if (index_.has_value()) { |
| index_->SetEntryDataHints(res_id, hints); |
| } |
| } |
| |
| std::optional<MemoryEntryDataHints> |
| SqlPersistentStore::BackendShard::GetInMemoryEntryDataHints( |
| CacheEntryKey::Hash key_hash) const { |
| return index_.has_value() ? index_->GetEntryDataHints(key_hash) |
| : std::nullopt; |
| } |
| |
| std::optional<SqlPersistentStore::ResId> |
| SqlPersistentStore::BackendShard::TryGetSingleResIdFromInMemoryIndex( |
| CacheEntryKey::Hash key_hash) const { |
| return index_.has_value() ? index_->TryGetSingleResId(key_hash) |
| : std::nullopt; |
| } |
| |
| // Like `WrapCallback`, but also updates the `store_status_`. |
| base::OnceCallback<void(SqlPersistentStore::ErrorAndStoreStatus)> |
| SqlPersistentStore::BackendShard::WrapCallbackWithStoreStatus( |
| ErrorCallback callback) { |
| return base::BindOnce( |
| [](base::WeakPtr<BackendShard> weak_ptr, ErrorCallback callback, |
| ErrorAndStoreStatus result) { |
| if (weak_ptr) { |
| weak_ptr->store_status_ = result.store_status; |
| // We should not run the callback when `this` was deleted. |
| std::move(callback).Run(std::move(result.result)); |
| } |
| }, |
| weak_factory_.GetWeakPtr(), std::move(callback)); |
| } |
| |
| base::OnceCallback<void(SqlPersistentStore::ResIdOrErrorAndStoreStatus)> |
| SqlPersistentStore::BackendShard::WrapCallbackWithStoreStatusAndIndexUpdate( |
| ResIdOrErrorCallback callback, |
| const CacheEntryKey& key, |
| bool is_new_entry, |
| const std::optional<MemoryEntryDataHints>& new_hints, |
| IndexMismatchLocation location) { |
| return base::BindOnce( |
| [](base::WeakPtr<BackendShard> weak_ptr, ResIdOrErrorCallback callback, |
| CacheEntryKey::Hash key_hash, bool is_new_entry, |
| const std::optional<MemoryEntryDataHints>& new_hints, |
| IndexMismatchLocation location, ResIdOrErrorAndStoreStatus result) { |
| if (weak_ptr) { |
| weak_ptr->store_status_ = result.store_status; |
| if (result.result.has_value() && weak_ptr->index_) { |
| if (is_new_entry) { |
| if (!weak_ptr->index_->Insert(key_hash, *result.result)) { |
| weak_ptr->RecordIndexMismatch(location); |
| } |
| } |
| if (new_hints) { |
| weak_ptr->index_->SetEntryDataHints(*result.result, *new_hints); |
| } |
| } |
| // We should not run the callback when `this` was deleted. |
| std::move(callback).Run(std::move(result.result)); |
| } |
| }, |
| weak_factory_.GetWeakPtr(), std::move(callback), key.hash(), is_new_entry, |
| new_hints, location); |
| } |
| |
| base::OnceCallback<void(SqlPersistentStore::EntryInfoOrErrorAndStoreStatus)> |
| SqlPersistentStore::BackendShard::WrapEntryInfoOrErrorCallback( |
| EntryInfoOrErrorCallback callback, |
| const CacheEntryKey& key, |
| IndexMismatchLocation location) { |
| return base::BindOnce( |
| [](base::WeakPtr<BackendShard> weak_ptr, |
| EntryInfoOrErrorCallback callback, CacheEntryKey::Hash key_hash, |
| IndexMismatchLocation location, |
| EntryInfoOrErrorAndStoreStatus result) { |
| if (weak_ptr) { |
| if (result.result.has_value() && weak_ptr->index_.has_value()) { |
| if (!result.result->opened) { |
| if (!weak_ptr->index_->Insert(key_hash, result.result->res_id)) { |
| weak_ptr->RecordIndexMismatch(location); |
| } |
| } |
| } |
| weak_ptr->store_status_ = result.store_status; |
| // We should not run the callback when `this` was deleted. |
| std::move(callback).Run(std::move(result.result)); |
| } |
| }, |
| weak_factory_.GetWeakPtr(), std::move(callback), key.hash(), location); |
| } |
| |
| base::OnceCallback<void(SqlPersistentStore::ResIdListOrErrorAndStoreStatus)> |
| SqlPersistentStore::BackendShard::WrapErrorCallbackToRemoveFromIndex( |
| ErrorCallback callback, |
| IndexMismatchLocation location) { |
| return base::BindOnce( |
| [](base::WeakPtr<BackendShard> weak_ptr, ErrorCallback callback, |
| IndexMismatchLocation location, |
| ResIdListOrErrorAndStoreStatus result) { |
| if (weak_ptr) { |
| if (result.result.has_value() && weak_ptr->index_.has_value()) { |
| for (ResId res_id : result.result.value()) { |
| if (!weak_ptr->index_->Remove(res_id)) { |
| weak_ptr->RecordIndexMismatch(location); |
| } |
| } |
| } |
| weak_ptr->store_status_ = result.store_status; |
| // We should not run the callback when `this` was deleted. |
| std::move(callback).Run( |
| std::move(result.result.error_or(Error::kOk))); |
| } |
| }, |
| weak_factory_.GetWeakPtr(), std::move(callback), location); |
| } |
| |
| void SqlPersistentStore::BackendShard::OnEvictionFinished( |
| ResIdListOrErrorCallback callback, |
| EvictionResultOrErrorAndStoreStatus result) { |
| if (result.result.has_value()) { |
| if (index_.has_value()) { |
| for (ResId res_id : result.result->deleted_res_ids) { |
| if (!index_->Remove(res_id)) { |
| RecordIndexMismatch(IndexMismatchLocation::kStartEviction); |
| } |
| } |
| } |
| pending_eviction_targets_ = |
| std::move(result.result->pending_eviction_targets); |
| } |
| store_status_ = result.store_status; |
| std::move(callback).Run( |
| result.result.has_value() |
| ? ResIdListOrError(std::move(result.result->deleted_res_ids)) |
| : base::unexpected(result.result.error())); |
| } |
| |
| void SqlPersistentStore::BackendShard::RecordIndexMismatch( |
| IndexMismatchLocation location) { |
| base::UmaHistogramEnumeration( |
| base::StrCat({kSqlDiskCacheBackendHistogramPrefix, "IndexMismatch"}), |
| location); |
| CHECK(!strict_corruption_check_enabled_); |
| } |
| |
| } // namespace disk_cache |