// 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.h"

#include <algorithm>
#include <atomic>
#include <functional>
#include <limits>
#include <memory>
#include <string_view>
#include <utility>
#include <vector>

#include "base/barrier_callback.h"
#include "base/barrier_closure.h"
#include "base/check.h"
#include "base/containers/flat_set.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/numerics/clamped_math.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/strcat.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/types/expected.h"
#include "net/base/cache_type.h"
#include "net/base/features.h"
#include "net/base/io_buffer.h"
#include "net/disk_cache/backend_cleanup_tracker.h"
#include "net/disk_cache/sql/cache_entry_key.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"
#include "net/disk_cache/sql/sql_persistent_store_backend_shard.h"
#include "net/disk_cache/sql/sql_read_cache_memory_monitor.h"
#include "net/disk_cache/sql/sql_shared_cache_manager.h"
#include "net/http/http_response_info.h"

namespace disk_cache {
namespace {

std::vector<base::flat_set<SqlPersistentStore::ResId>> GroupResIdPerShardId(
    std::vector<SqlPersistentStore::ResIdAndShardId> excluded_list,
    size_t size_of_shards) {
  std::vector<std::vector<SqlPersistentStore::ResId>> res_id_lists(
      size_of_shards);
  for (const auto& res_id_and_shard_id : excluded_list) {
    res_id_lists[res_id_and_shard_id.shard_id.value()].emplace_back(
        res_id_and_shard_id.res_id);
  }
  std::vector<base::flat_set<SqlPersistentStore::ResId>> res_id_sets;
  for (size_t i = 0; i < size_of_shards; ++i) {
    std::sort(res_id_lists[i].begin(), res_id_lists[i].end());
    res_id_sets.emplace_back(base::sorted_unique, std::move(res_id_lists[i]));
  }
  return res_id_sets;
}

// Calculates the maximum size for a single cache entry's data.
int64_t CalculateMaxFileSize(int64_t max_bytes) {
  return std::max(base::saturated_cast<int64_t>(
                      max_bytes / kSqlBackendMaxFileRatioDenominator),
                  kSqlBackendMinFileSizeLimit);
}

// Appends `result` to `results` and returns `results`.
// This is used as a helper for base::BindOnce to chain callbacks.
std::vector<bool> AppendResult(std::vector<bool> results, bool result) {
  results.push_back(result);
  return results;
}

void RecordEvictionHistograms(std::string_view method_name,
                              SqlPersistentStore::Error error,
                              base::TimeTicks start_time,
                              size_t entry_count,
                              bool reduce_uma) {
  if (reduce_uma) {
    return;
  }
  base::UmaHistogramMicrosecondsTimes(
      base::StrCat({kSqlDiskCacheBackendHistogramPrefix, method_name,
                    error == SqlPersistentStore::Error::kOk ? ".SuccessTime"
                                                            : ".FailureTime"}),
      base::TimeTicks::Now() - start_time);
  base::UmaHistogramEnumeration(
      base::StrCat(
          {kSqlDiskCacheBackendHistogramPrefix, method_name, ".Result"}),
      error);
  if (error == SqlPersistentStore::Error::kOk) {
    base::UmaHistogramCounts1000(
        base::StrCat(
            {kSqlDiskCacheBackendHistogramPrefix, method_name, ".EntryCount"}),
        entry_count);
  }
}

}  // namespace

SqlPersistentStore::SharedCacheEligibleEntry::SharedCacheEligibleEntry() =
    default;
SqlPersistentStore::SharedCacheEligibleEntry::~SharedCacheEligibleEntry() =
    default;
SqlPersistentStore::SharedCacheEligibleEntry::SharedCacheEligibleEntry(
    SqlPersistentStore::SharedCacheEligibleEntry&&) = default;
SqlPersistentStore::SharedCacheEligibleEntry&
SqlPersistentStore::SharedCacheEligibleEntry::operator=(
    SqlPersistentStore::SharedCacheEligibleEntry&&) = default;

SqlPersistentStore::SharedCacheEligibleEntry::SharedCacheEligibleEntry(
    CacheEntryKey key,
    GURL url,
    std::unique_ptr<net::HttpResponseInfo> response_info,
    net::NetworkIsolationKey nik)
    : key(std::move(key)),
      url(std::move(url)),
      response_info(std::move(response_info)),
      nik(std::move(nik)) {}

// static
std::vector<std::unique_ptr<SqlPersistentStore::BackendShard>>
SqlPersistentStore::CreateBackendShards(
    const base::FilePath& path,
    net::CacheType type,
    bool shared_cache_enabled,
    scoped_refptr<SqlReadCacheMemoryMonitor> read_cache_memory_monitor,
    std::vector<scoped_refptr<base::SequencedTaskRunner>>
        background_task_runners,
    SqlAsyncTaskManager& async_task_manager,
    scoped_refptr<BackendCleanupTracker> cleanup_tracker) {
  const size_t num_shards = background_task_runners.size();
  CHECK(num_shards < std::numeric_limits<ShardId::underlying_type>::max());
  std::vector<std::unique_ptr<BackendShard>> backend_shards;
  backend_shards.reserve(num_shards);
  for (size_t i = 0; i < num_shards; ++i) {
    backend_shards.emplace_back(std::make_unique<BackendShard>(
        ShardId(i), path, type, shared_cache_enabled, read_cache_memory_monitor,
        background_task_runners[i], async_task_manager, cleanup_tracker));
  }
  return backend_shards;
}

SqlPersistentStore::SqlPersistentStore(
    const base::FilePath& path,
    int64_t max_bytes,
    net::CacheType type,
    std::vector<scoped_refptr<base::SequencedTaskRunner>>
        background_task_runners,
    SqlAsyncTaskManager& async_task_manager,
    scoped_refptr<BackendCleanupTracker> cleanup_tracker)
    : background_task_runners_(std::move(background_task_runners)),
      async_task_manager_(async_task_manager),
      read_cache_memory_monitor_(
          base::MakeRefCounted<SqlReadCacheMemoryMonitor>(
              net::features::kSqlDiskCacheMaxReadBufferTotalSize.Get())),
      shared_cache_manager_(base::FeatureList::IsEnabled(
                                net::features::kRendererAccessibleHttpCache)
                                ? std::make_unique<SqlSharedCacheManager>(
                                      *this,
                                      path,
                                      read_cache_memory_monitor_,
                                      cleanup_tracker)
                                : nullptr),
      backend_shards_(
          CreateBackendShards(path,
                              type,
                              /*shared_cache_enabled=*/!!shared_cache_manager_,
                              read_cache_memory_monitor_,
                              background_task_runners_,
                              async_task_manager,
                              std::move(cleanup_tracker))),
      user_max_bytes_(max_bytes),
      reduce_uma_(net::features::kSqlDiskCacheReduceUma.Get()) {}
SqlPersistentStore::~SqlPersistentStore() = default;

void SqlPersistentStore::Initialize(ErrorCallback callback) {
  // 1 task for backend shards init, plus 1 task for shared_cache_manager_ init.
  const size_t num_init_tasks = 1 + (shared_cache_manager_ ? 1 : 0);
  auto barrier_callback =
      CreateBarrierErrorCallback(std::move(callback), num_init_tasks);

  if (net::features::kSqlDiskCacheSerialInitialize.Get()) {
    std::vector<InitResultOrError> results;
    results.reserve(GetSizeOfShards());
    InitializeNextShard(barrier_callback, std::move(results));
  } else {
    auto shard_barrier_callback = base::BarrierCallback<InitResultOrError>(
        GetSizeOfShards(),
        base::BindOnce(&SqlPersistentStore::OnInitializeFinished,
                       weak_factory_.GetWeakPtr(), barrier_callback));
    for (const auto& backend_shard : backend_shards_) {
      backend_shard->Initialize(user_max_bytes_, shard_barrier_callback);
    }
  }

  if (shared_cache_manager_) {
    shared_cache_manager_->Init(
        base::BindOnce(
            [](base::expected<void, SqlSharedCacheIndexDatabase::Error> result)
                -> Error {
              return result.has_value()
                         ? Error::kOk
                         : Error::kFailedToInitializeSharedCacheIndexDatabase;
            })
            .Then(barrier_callback));
  }
}

void SqlPersistentStore::InitializeNextShard(
    ErrorCallback callback,
    std::vector<InitResultOrError> results) {
  if (results.size() == GetSizeOfShards()) {
    OnInitializeFinished(std::move(callback), std::move(results));
    return;
  }
  const size_t shard_index = results.size();
  backend_shards_[shard_index]->Initialize(
      user_max_bytes_, base::BindOnce(&SqlPersistentStore::OnShardInitialized,
                                      weak_factory_.GetWeakPtr(),
                                      std::move(callback), std::move(results)));
}

void SqlPersistentStore::OnShardInitialized(
    ErrorCallback callback,
    std::vector<InitResultOrError> results,
    InitResultOrError result) {
  if (!result.has_value()) {
    std::move(callback).Run(result.error());
    return;
  }
  results.push_back(std::move(result));
  InitializeNextShard(std::move(callback), std::move(results));
}

void SqlPersistentStore::OpenOrCreateEntry(const CacheEntryKey& key,
                                           EntryInfoOrErrorCallback callback) {
  GetShard(key).OpenOrCreateEntry(
      key,
      base::BindOnce(&SqlPersistentStore::OnOpenEntryFinished,
                     weak_factory_.GetWeakPtr(), key,
                     OpenEntryMode::kOpenOrCreateEntry, std::move(callback)));
}

void SqlPersistentStore::OpenEntry(const CacheEntryKey& key,
                                   EntryInfoOrErrorCallback callback) {
  GetShard(key).OpenEntry(
      key, base::BindOnce(&SqlPersistentStore::OnOpenEntryFinished,
                          weak_factory_.GetWeakPtr(), key,
                          OpenEntryMode::kOpenEntry, std::move(callback)));
}

void SqlPersistentStore::OnOpenEntryFinished(CacheEntryKey key,
                                             OpenEntryMode mode,
                                             EntryInfoOrErrorCallback callback,
                                             EntryInfoOrError result) {
  if (!result.has_value()) {
    std::move(callback).Run(std::move(result));
    return;
  }
  EntryInfo entry_info = std::move(*result);

  // If the entry does not use shared cache, return `entry_info` immediately.
  if (!entry_info.shared_cache_resource_id.has_value()) {
    std::move(callback).Run(std::move(entry_info));
    return;
  }

  CHECK(shared_cache_manager_);
  SqlSharedCacheDbId db_id = entry_info.shared_cache_resource_id->db_id;
  SqlSharedCacheRowId row_id = entry_info.shared_cache_resource_id->row_id;
  int body_size = base::saturated_cast<int>(entry_info.body_end);

  // Asynchronously fetch the SqlSharedCacheHandle for `db_id`.
  shared_cache_manager_->GetCacheByDbId(
      db_id,
      base::BindOnce(
          [](base::WeakPtr<SqlPersistentStore> weak_ptr, CacheEntryKey key,
             SqlSharedCacheRowId row_id, int body_size, EntryInfo entry_info,
             OpenEntryMode mode, EntryInfoOrErrorCallback callback,
             scoped_refptr<SqlSharedCacheHandle> handle) {
            if (!weak_ptr) {
              return;
            }
            if (!handle || !(*handle)) {
              weak_ptr->OnSharedCacheFetchFailed(key, mode,
                                                 std::move(callback));
              return;
            }
            entry_info.shared_cache_handle = handle;
            // Asynchronously fetch the SqlSharedCacheBlobHandle for `row_id`.
            (*handle)->GetBlobHandle(
                key, row_id, body_size,
                base::BindOnce(
                    [](base::WeakPtr<SqlPersistentStore> weak_ptr,
                       CacheEntryKey key, EntryInfo entry_info,
                       OpenEntryMode mode, EntryInfoOrErrorCallback callback,
                       base::expected<scoped_refptr<SqlSharedCacheBlobHandle>,
                                      SqlSharedCacheIsolatedDatabase::Error>
                           blob_handle_result) {
                      if (!weak_ptr) {
                        return;
                      }
                      if (!blob_handle_result.has_value()) {
                        weak_ptr->OnSharedCacheFetchFailed(key, mode,
                                                           std::move(callback));
                        return;
                      }
                      entry_info.shared_cache_blob_handle =
                          std::move(*blob_handle_result);
                      std::move(callback).Run(std::move(entry_info));
                    },
                    weak_ptr, key, std::move(entry_info), mode,
                    std::move(callback)));
          },
          weak_factory_.GetWeakPtr(), key, row_id, body_size,
          std::move(entry_info), mode, std::move(callback)));
}

void SqlPersistentStore::OnSharedCacheFetchFailed(
    const CacheEntryKey& key,
    OpenEntryMode mode,
    EntryInfoOrErrorCallback callback) {
  // Shared cache handle or blob handle retrieval failed. Delete the invalid
  // live entry from the store shard.
  DeleteLiveEntry(
      key,
      base::BindOnce(
          [](base::WeakPtr<SqlPersistentStore> weak_ptr, CacheEntryKey key,
             OpenEntryMode mode, EntryInfoOrErrorCallback callback,
             Error error) {
            if (!weak_ptr) {
              return;
            }
            switch (mode) {
              case OpenEntryMode::kOpenOrCreateEntry:
                // For OpenOrCreateEntry: now that the invalid entry has
                // been deleted, recreate a brand new entry for `key`.
                weak_ptr->CreateEntry(key, base::Time::Now(),
                                      std::move(callback));
                break;
              case OpenEntryMode::kOpenEntry:
                // For OpenEntry: return kNotFound.
                std::move(callback).Run(base::unexpected(Error::kNotFound));
                break;
            }
          },
          weak_factory_.GetWeakPtr(), key, mode, std::move(callback)));
}

void SqlPersistentStore::CreateEntry(const CacheEntryKey& key,
                                     base::Time creation_time,
                                     EntryInfoOrErrorCallback callback) {
  GetShard(key).CreateEntry(key, creation_time, std::move(callback));
}

void SqlPersistentStore::DoomEntry(const CacheEntryKey& key,
                                   ResId res_id,
                                   bool accept_index_mismatch,
                                   ErrorCallback callback) {
  GetShard(key).DoomEntry(key, res_id, accept_index_mismatch,
                          std::move(callback));
}

void SqlPersistentStore::DeleteDoomedEntry(const CacheEntryKey& key,
                                           ResId res_id,
                                           ErrorCallback callback) {
  GetShard(key).DeleteDoomedEntry(
      key, res_id,
      WrapCallbackWithSingleSharedCacheDelete(std::move(callback)));
}

void SqlPersistentStore::DeleteLiveEntry(const CacheEntryKey& key,
                                         ErrorCallback callback) {
  GetShard(key).DeleteLiveEntry(
      key, WrapCallbackWithSharedCacheDelete(std::move(callback)));
}

void SqlPersistentStore::DeleteAllEntries(ErrorCallback callback) {
  auto barrier_callback = CreateBarrierErrorCallback(std::move(callback));
  for (const auto& backend_shard : backend_shards_) {
    backend_shard->DeleteAllEntries(barrier_callback);
  }
}

void SqlPersistentStore::DeleteLiveEntriesBetween(
    base::Time initial_time,
    base::Time end_time,
    std::vector<ResIdAndShardId> excluded_list,
    ErrorCallback callback) {
  auto barrier_callback =
      base::BarrierCallback<DeletedSharedCacheResourcesOrError>(
          GetSizeOfShards(),
          base::BindOnce(
              [](base::WeakPtr<SqlPersistentStore> weak_ptr,
                 ErrorCallback callback,
                 std::vector<DeletedSharedCacheResourcesOrError> results) {
                Error first_error = Error::kOk;
                std::vector<SqlSharedCacheResourceId> all_deleted_resources;
                for (auto& res : results) {
                  if (res.has_value()) {
                    all_deleted_resources.insert(
                        all_deleted_resources.end(),
                        std::make_move_iterator(res->begin()),
                        std::make_move_iterator(res->end()));
                  } else if (first_error == Error::kOk) {
                    first_error = res.error();
                  }
                }
                if (weak_ptr && !all_deleted_resources.empty()) {
                  weak_ptr->OnSharedCacheResourcesDeleted(
                      std::move(all_deleted_resources));
                }
                std::move(callback).Run(first_error);
              },
              weak_factory_.GetWeakPtr(), std::move(callback)));
  auto res_id_sets =
      GroupResIdPerShardId(std::move(excluded_list), GetSizeOfShards());
  for (size_t i = 0; i < GetSizeOfShards(); ++i) {
    backend_shards_[i]->DeleteLiveEntriesBetween(
        initial_time, end_time, std::move(res_id_sets[i]), barrier_callback);
  }
}

void SqlPersistentStore::UpdateEntryLastUsedByKey(const CacheEntryKey& key,
                                                  base::Time last_used,
                                                  ErrorCallback callback) {
  GetShard(key).UpdateEntryLastUsedByKey(key, last_used, std::move(callback));
}

void SqlPersistentStore::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) {
  GetShard(key).WriteEntryDataAndMetadata(
      key, res_id, old_body_end, std::move(buffer), last_used, new_hints,
      std::move(head_buffer), header_size_delta, doomed_new_entry,
      std::move(callback));
}

void SqlPersistentStore::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,
    bool sparse_write,
    int64_t header_size,
    ResIdOrErrorCallback callback) {
  const int64_t max_sparse_data_size =
      max_bytes_ / kSqlBackendMaxSparseDataRatioDenominator;
  GetShard(key).WriteEntryData(key, res_id_or_last_used_time, old_body_end,
                               std::move(buffer), truncate, doomed_new_entry,
                               sparse_write, header_size, max_sparse_data_size,
                               std::move(callback));
}

void SqlPersistentStore::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,
                                       ReadResultOrErrorCallback callback) {
  GetShard(key).ReadEntryData(key, res_id, offset, std::move(buffer), buf_len,
                              body_end, sparse_reading, std::move(callback));
}

void SqlPersistentStore::MoveBlobsToSharedCache(
    const CacheEntryKey& key,
    ResId res_id,
    SqlSharedCacheResourceId shared_cache_resource_id,
    ErrorCallback callback) {
  GetShard(key).MoveBlobsToSharedCache(key, res_id, shared_cache_resource_id,
                                       std::move(callback));
}

void SqlPersistentStore::GetEntryAvailableRange(const CacheEntryKey& key,
                                                ResId res_id,
                                                int64_t offset,
                                                int len,
                                                RangeResultCallback callback) {
  GetShard(key).GetEntryAvailableRange(key, res_id, offset, len,
                                       std::move(callback));
}

void SqlPersistentStore::CalculateSizeOfEntriesBetween(
    base::Time initial_time,
    base::Time end_time,
    Int64OrErrorCallback callback) {
  auto barrier_callback = base::BarrierCallback<Int64OrError>(
      GetSizeOfShards(),
      base::BindOnce(
          [](Int64OrErrorCallback callback, std::vector<Int64OrError> results) {
            int64_t total_size = 0;
            for (const auto& result : results) {
              if (!result.has_value()) {
                std::move(callback).Run(base::unexpected(result.error()));
                return;
              }
              total_size += result.value();
            }
            std::move(callback).Run(total_size);
          },
          std::move(callback)));
  for (const auto& backend_shard : backend_shards_) {
    backend_shard->CalculateSizeOfEntriesBetween(initial_time, end_time,
                                                 barrier_callback);
  }
}

void SqlPersistentStore::OpenNextEntry(
    const EntryIterator& iterator,
    OptionalEntryInfoWithKeyAndIteratorCallback callback) {
  if (iterator.value().shard_id.value() >= GetSizeOfShards()) {
    std::move(callback).Run(std::nullopt);
    return;
  }
  backend_shards_[iterator.value().shard_id.value()]->OpenNextEntry(
      iterator,
      base::BindOnce(
          [](base::WeakPtr<SqlPersistentStore> weak_ptr, ShardId shard_id,
             OptionalEntryInfoWithKeyAndIteratorCallback callback,
             OptionalEntryInfoWithKeyAndIterator result) {
            if (!weak_ptr) {
              return;
            }
            if (result.has_value()) {
              std::move(callback).Run(std::move(result));
              return;
            }
            EntryIterator new_iterator;
            new_iterator.value().shard_id = ShardId(shard_id.value() + 1);
            weak_ptr->OpenNextEntry(new_iterator, std::move(callback));
          },
          weak_factory_.GetWeakPtr(), iterator.value().shard_id,
          std::move(callback)));
}

SqlPersistentStore::EvictionUrgency SqlPersistentStore::GetEvictionUrgency() {
  if (eviction_in_progress_) {
    return EvictionUrgency::kNotNeeded;
  }
  if (HasPendingEviction()) {
    return EvictionUrgency::kNeeded;
  }
  // Checks if the total size of entries exceeds the high watermark and the
  // database is open, to determine if eviction should be initiated.
  const int64_t current_size = GetSizeOfAllEntries();
  if (current_size > high_watermark_) {
    return EvictionUrgency::kNeeded;
  }
  if (current_size > idle_time_high_watermark_) {
    return EvictionUrgency::kIdleTime;
  }
  return EvictionUrgency::kNotNeeded;
}

void SqlPersistentStore::StartEviction(
    std::vector<ResIdAndShardId> excluded_list,
    bool is_idle_time_eviction,
    scoped_refptr<base::RefCountedData<std::atomic_bool>> eviction_abort_flag,
    ErrorCallback callback) {
  CHECK(!eviction_in_progress_);
  eviction_in_progress_ = true;
  auto new_callback = base::BindOnce(
      [](base::WeakPtr<SqlPersistentStore> weak_ptr, ErrorCallback callback,
         Error result) {
        if (weak_ptr) {
          weak_ptr->eviction_in_progress_ = false;
        }
        std::move(callback).Run(result);
      },
      weak_factory_.GetWeakPtr(), std::move(callback));
  MaybeLoadInMemoryIndex(base::BindOnce(
      &SqlPersistentStore::StartEvictionInternal, weak_factory_.GetWeakPtr(),
      std::move(excluded_list), is_idle_time_eviction,
      std::move(eviction_abort_flag), std::move(new_callback)));
}

void SqlPersistentStore::StartEvictionInternal(
    std::vector<ResIdAndShardId> excluded_list,
    bool is_idle_time_eviction,
    scoped_refptr<base::RefCountedData<std::atomic_bool>> eviction_abort_flag,
    ErrorCallback callback,
    Error load_index_result) {
  if (load_index_result != Error::kOk) {
    std::move(callback).Run(load_index_result);
    return;
  }
  auto excluded_res_id_sets =
      GroupResIdPerShardId(std::move(excluded_list), GetSizeOfShards());
  if (HasPendingEviction()) {
    ResumePendingEviction(std::move(excluded_res_id_sets),
                          is_idle_time_eviction, std::move(eviction_abort_flag),
                          std::move(callback));
  } else {
    StartNewEviction(std::move(excluded_res_id_sets), is_idle_time_eviction,
                     std::move(eviction_abort_flag), std::move(callback));
  }
}

void SqlPersistentStore::ResumePendingEviction(
    std::vector<base::flat_set<SqlPersistentStore::ResId>> excluded_res_id_sets,
    bool is_idle_time_eviction,
    scoped_refptr<base::RefCountedData<std::atomic_bool>> eviction_abort_flag,
    ErrorCallback callback) {
  const auto size_of_all_entries = GetSizeOfAllEntries();
  // If the size is less than the high watermark and the abort flag is already
  // true, return OK before posting tasks to each shard.
  if (size_of_all_entries <= high_watermark_ &&
      eviction_abort_flag->data.load(std::memory_order_relaxed)) {
    std::move(callback).Run(Error::kOk);
    return;
  }
  auto remaining_mandatory_size =
      base::MakeRefCounted<base::RefCountedData<std::atomic_int64_t>>(
          std::in_place,
          std::max<int64_t>(size_of_all_entries - high_watermark_, 0));
  auto barrier_callback = base::BarrierCallback<EvictionResult>(
      GetSizeOfShards(),
      base::BindOnce(&SqlPersistentStore::OnPendingEvictionFinished,
                     weak_factory_.GetWeakPtr(), excluded_res_id_sets,
                     is_idle_time_eviction, eviction_abort_flag,
                     base::TimeTicks::Now(), std::move(callback)));
  for (size_t i = 0; i < GetSizeOfShards(); ++i) {
    backend_shards_[i]->ResumePendingEviction(
        std::move(excluded_res_id_sets[i]), is_idle_time_eviction,
        eviction_abort_flag, remaining_mandatory_size, barrier_callback);
  }
}

void SqlPersistentStore::OnPendingEvictionFinished(
    std::vector<base::flat_set<SqlPersistentStore::ResId>> excluded_res_id_sets,
    bool is_idle_time_eviction,
    scoped_refptr<base::RefCountedData<std::atomic_bool>> eviction_abort_flag,
    base::TimeTicks start_time,
    ErrorCallback callback,
    std::vector<EvictionResult> results) {
  Error error = Error::kOk;
  size_t count = 0;
  std::vector<SqlSharedCacheResourceId> all_deleted_resources;
  for (auto& result : results) {
    if (result.error != Error::kOk) {
      error = result.error;
    }
    count += result.evicted_entry_count;
    if (!result.deleted_shared_cache_resources.empty()) {
      all_deleted_resources.insert(
          all_deleted_resources.end(),
          std::make_move_iterator(
              result.deleted_shared_cache_resources.begin()),
          std::make_move_iterator(result.deleted_shared_cache_resources.end()));
    }
  }

  if (!all_deleted_resources.empty()) {
    OnSharedCacheResourcesDeleted(std::move(all_deleted_resources));
  }

  RecordEvictionHistograms(
      is_idle_time_eviction ? "ResumeEvictionOnIdleTime" : "ResumeEviction",
      error, start_time, count, reduce_uma_);

  if (error != Error::kOk || HasPendingEviction()) {
    std::move(callback).Run(error);
    return;
  }
  StartNewEviction(std::move(excluded_res_id_sets), is_idle_time_eviction,
                   std::move(eviction_abort_flag), std::move(callback));
}

void SqlPersistentStore::StartNewEviction(
    std::vector<base::flat_set<SqlPersistentStore::ResId>> excluded_res_id_sets,
    bool is_idle_time_eviction,
    scoped_refptr<base::RefCountedData<std::atomic_bool>> eviction_abort_flag,
    ErrorCallback callback) {
  CHECK(!eviction_result_callback_);
  CHECK(eviction_abort_flag);
  CHECK(callback);
  const auto size_of_all_entries = GetSizeOfAllEntries();
  if (size_of_all_entries <=
      (is_idle_time_eviction ? idle_time_high_watermark_ : high_watermark_)) {
    std::move(callback).Run(Error::kOk);
    return;
  }
  const int64_t size_to_be_removed = size_of_all_entries - low_watermark_;
  CHECK(size_to_be_removed > 0);
  auto remaining_mandatory_size =
      base::MakeRefCounted<base::RefCountedData<std::atomic_int64_t>>(
          std::in_place,
          std::max<int64_t>(size_of_all_entries - high_watermark_, 0));
  eviction_result_callback_ = std::move(callback);
  auto barrier_callback = base::BarrierCallback<EvictionResult>(
      GetSizeOfShards(),
      base::BindOnce(&SqlPersistentStore::OnEvictionFinished,
                     weak_factory_.GetWeakPtr(), is_idle_time_eviction,
                     base::TimeTicks::Now()));
  auto aggregator = base::MakeRefCounted<EvictionCandidateAggregator>(
      size_to_be_removed, background_task_runners_);
  for (size_t i = 0; i < GetSizeOfShards(); ++i) {
    backend_shards_[i]->StartEviction(
        size_to_be_removed, std::move(excluded_res_id_sets[i]),
        is_idle_time_eviction, aggregator, eviction_abort_flag,
        remaining_mandatory_size, barrier_callback);
  }
}

void SqlPersistentStore::OnEvictionFinished(
    bool is_idle_time_eviction,
    base::TimeTicks start_time,
    std::vector<EvictionResult> results) {
  Error error = Error::kOk;
  size_t count = 0;
  std::vector<SqlSharedCacheResourceId> all_deleted_resources;
  for (auto& result : results) {
    if (result.error != Error::kOk) {
      error = result.error;
    }
    count += result.evicted_entry_count;
    if (!result.deleted_shared_cache_resources.empty()) {
      all_deleted_resources.insert(
          all_deleted_resources.end(),
          std::make_move_iterator(
              result.deleted_shared_cache_resources.begin()),
          std::make_move_iterator(result.deleted_shared_cache_resources.end()));
    }
  }

  if (!all_deleted_resources.empty()) {
    OnSharedCacheResourcesDeleted(std::move(all_deleted_resources));
  }

  RecordEvictionHistograms(
      is_idle_time_eviction ? "RunNewEvictionOnIdleTime" : "RunNewEviction",
      error, start_time, count, reduce_uma_);

  CHECK(eviction_result_callback_);
  auto callback = std::move(eviction_result_callback_);
  eviction_result_callback_.Reset();
  std::move(callback).Run(error);
}

bool SqlPersistentStore::HasPendingEviction() const {
  return std::ranges::any_of(backend_shards_, [](const auto& backend_shard) {
    return backend_shard->HasPendingEviction();
  });
}

int64_t SqlPersistentStore::MaxFileSize() const {
  return max_file_size_;
}

int64_t SqlPersistentStore::MaxSize() const {
  return max_bytes_;
}

int32_t SqlPersistentStore::GetEntryCount() const {
  base::ClampedNumeric<int32_t> result;
  for (const auto& backend_shard : backend_shards_) {
    result += backend_shard->GetEntryCount();
  }
  return result;
}

void SqlPersistentStore::GetEntryCountAsync(Int32Callback callback) const {
  auto barrier_callback = base::BarrierCallback<int32_t>(
      GetSizeOfShards(),
      base::BindOnce(
          [](Int32Callback callback, const std::vector<int32_t>& results) {
            int32_t total_count = 0;
            for (const auto& result : results) {
              total_count += result;
            }
            std::move(callback).Run(total_count);
          },
          std::move(callback)));
  for (const auto& backend_shard : backend_shards_) {
    backend_shard->GetEntryCountAsync(barrier_callback);
  }
}

int64_t SqlPersistentStore::GetSizeOfAllEntries() const {
  base::ClampedNumeric<int64_t> result;
  for (const auto& backend_shard : backend_shards_) {
    result += backend_shard->GetSizeOfAllEntries();
  }
  return result;
}

void SqlPersistentStore::MaybeLoadInMemoryIndex(ErrorCallback callback) {
  if (in_memory_load_result_.has_value()) {
    std::move(callback).Run(*in_memory_load_result_);
    return;
  }
  pending_in_memory_load_result_callbacks_.push_back(std::move(callback));
  if (!in_memory_load_triggered_) {
    in_memory_load_triggered_ = true;
    auto barrier_callback = CreateBarrierErrorCallback(
        base::BindOnce(&SqlPersistentStore::OnLoadInMemoryIndexFinished,
                       weak_factory_.GetWeakPtr()));
    for (const auto& backend_shard : backend_shards_) {
      backend_shard->LoadInMemoryIndex(barrier_callback);
    }
  }
}

void SqlPersistentStore::OnLoadInMemoryIndexFinished(Error result) {
  CHECK(!in_memory_load_result_.has_value());
  in_memory_load_result_ = result;
  auto callbacks = std::move(pending_in_memory_load_result_callbacks_);
  pending_in_memory_load_result_callbacks_.clear();
  for (auto& callback : callbacks) {
    std::move(callback).Run(result);
  }
}

bool SqlPersistentStore::MaybeRunCleanupDoomedEntries(ErrorCallback callback) {
  auto barrier_callback = CreateBarrierErrorCallback(std::move(callback));
  size_t sync_return_count = 0;
  for (const auto& backend_shard : backend_shards_) {
    if (!backend_shard->MaybeRunCleanupDoomedEntries(
            WrapCallbackWithSharedCacheDelete(barrier_callback))) {
      // If a shard completes synchronously, it returns false. Count how many do
      // so.
      ++sync_return_count;
    }
  }
  if (sync_return_count == GetSizeOfShards()) {
    // If all shards completed synchronously, no cleanup task was scheduled.
    return false;
  }
  for (size_t i = 0; i < sync_return_count; ++i) {
    barrier_callback.Run(Error::kOk);
  }
  // Reaching this point means at least one shard scheduled a task.
  return true;
}

void SqlPersistentStore::MaybeRunCheckpoint(
    base::OnceCallback<void(bool)> callback) {
  if (net::features::kSqlDiskCacheSerialCheckpoint.Get()) {
    std::vector<bool> results;
    results.reserve(backend_shards_.size());
    RunNextCheckpoint(std::move(callback), std::move(results));
    return;
  }
  auto barrier_callback = base::BarrierCallback<bool>(
      GetSizeOfShards(), base::BindOnce([](std::vector<bool> results) {
                           return std::ranges::any_of(results, std::identity{});
                         }).Then(std::move(callback)));
  for (const auto& backend_shard : backend_shards_) {
    backend_shard->MaybeRunCheckpoint(barrier_callback);
  }
}

void SqlPersistentStore::RunNextCheckpoint(
    base::OnceCallback<void(bool)> callback,
    std::vector<bool> results) {
  if (results.size() == backend_shards_.size()) {
    std::move(callback).Run(std::ranges::any_of(results, std::identity{}));
    return;
  }
  const auto index = results.size();
  backend_shards_[index]->MaybeRunCheckpoint(
      base::BindOnce(&AppendResult, std::move(results))
          .Then(base::BindOnce(&SqlPersistentStore::RunNextCheckpoint,
                               weak_factory_.GetWeakPtr(),
                               std::move(callback))));
}

void SqlPersistentStore::MaybeRunIncrementalVacuum(
    scoped_refptr<base::RefCountedData<std::atomic_bool>> abort_flag,
    base::OnceCallback<void(bool)> callback) {
  if (!net::features::kSqlDiskCacheIncrementalVacuum.Get()) {
    std::move(callback).Run(false);
    return;
  }
  auto barrier_callback = base::BarrierCallback<bool>(
      GetSizeOfShards(), base::BindOnce(
                             [](base::OnceCallback<void(bool)> callback,
                                const std::vector<bool>& results) {
                               std::move(callback).Run(std::ranges::all_of(
                                   results, std::identity{}));
                             },
                             std::move(callback)));
  for (const auto& backend_shard : backend_shards_) {
    backend_shard->MaybeRunIncrementalVacuum(abort_flag, barrier_callback);
  }
}

void SqlPersistentStore::OnSharedCacheResourcesDeleted(
    std::vector<SqlSharedCacheResourceId> ids) {
  if (ids.empty() || !shared_cache_manager_) {
    return;
  }
  shared_cache_manager_->DeleteResources(std::move(ids), base::DoNothing());
}

SqlPersistentStore::DeletedSharedCacheResourceOrErrorCallback
SqlPersistentStore::WrapCallbackWithSingleSharedCacheDelete(
    ErrorCallback callback) {
  return base::BindOnce(
      [](base::WeakPtr<SqlPersistentStore> store, ErrorCallback callback,
         DeletedSharedCacheResourceOrError result) {
        if (store && result.has_value() && result->has_value()) {
          store->OnSharedCacheResourcesDeleted({**result});
        }
        std::move(callback).Run(result.error_or(Error::kOk));
      },
      weak_factory_.GetWeakPtr(), std::move(callback));
}

SqlPersistentStore::DeletedSharedCacheResourcesOrErrorCallback
SqlPersistentStore::WrapCallbackWithSharedCacheDelete(ErrorCallback callback) {
  return base::BindOnce(
      [](base::WeakPtr<SqlPersistentStore> store, ErrorCallback callback,
         DeletedSharedCacheResourcesOrError result) {
        if (store && result.has_value() && !result->empty()) {
          store->OnSharedCacheResourcesDeleted(std::move(*result));
        }
        std::move(callback).Run(result.error_or(Error::kOk));
      },
      weak_factory_.GetWeakPtr(), std::move(callback));
}

void SqlPersistentStore::EnableStrictCorruptionCheckForTesting() {
  for (const auto& backend_shard : backend_shards_) {
    backend_shard->EnableStrictCorruptionCheckForTesting();  // IN-TEST
  }
}

void SqlPersistentStore::SetSimulateDbFailureForTesting(bool fail) {
  for (const auto& backend_shard : backend_shards_) {
    backend_shard->SetSimulateDbFailureForTesting(fail);  // IN-TEST
  }
}

void SqlPersistentStore::SetSimulateDbShardFailureForTesting(  // IN-TEST
    size_t shard_index,
    bool fail) {
  CHECK_LT(shard_index, backend_shards_.size());
  backend_shards_[shard_index]->SetSimulateDbFailureForTesting(  // IN-TEST
      fail);
}

void SqlPersistentStore::RazeAndPoisonForTesting() {
  for (const auto& backend_shard : backend_shards_) {
    backend_shard->RazeAndPoisonForTesting();  // IN-TEST
  }
}

void SqlPersistentStore::SetEvictionHookForTesting(  // IN-TEST
    base::RepeatingClosure hook) {
  for (auto& shard : backend_shards_) {
    shard->SetEvictionHookForTesting(hook);  // IN-TEST
  }
}

SqlPersistentStore::IndexState SqlPersistentStore::GetIndexStateForHash(
    CacheEntryKey::Hash key_hash) const {
  return GetShard(key_hash).GetIndexStateForHash(key_hash);
}

void SqlPersistentStore::SetInMemoryEntryDataHints(CacheEntryKey::Hash key_hash,
                                                   ResId res_id,
                                                   MemoryEntryDataHints hints) {
  return GetShard(key_hash).SetInMemoryEntryDataHints(key_hash, res_id, hints);
}

std::optional<MemoryEntryDataHints>
SqlPersistentStore::GetInMemoryEntryDataHints(
    CacheEntryKey::Hash key_hash) const {
  return GetShard(key_hash).GetInMemoryEntryDataHints(key_hash);
}

SqlPersistentStore::ShardId SqlPersistentStore::GetShardIdForHash(
    CacheEntryKey::Hash key_hash) const {
  return ShardId(key_hash.value() % GetSizeOfShards());
}

void SqlPersistentStore::SetMaxSize(int64_t max_bytes) {
  max_bytes_ = max_bytes;
  high_watermark_ = max_bytes * kSqlBackendEvictionHighWaterMarkPermille / 1000;
  idle_time_high_watermark_ =
      max_bytes * kSqlBackendIdleTimeEvictionHighWaterMarkPermille / 1000;
  low_watermark_ = max_bytes * kSqlBackendEvictionLowWaterMarkPermille / 1000;
  max_file_size_ = CalculateMaxFileSize(max_bytes);
}

base::RepeatingCallback<void(SqlPersistentStore::Error)>
SqlPersistentStore::CreateBarrierErrorCallback(ErrorCallback callback,
                                               size_t num_tasks) {
  return base::BarrierCallback<Error>(
      num_tasks == 0 ? GetSizeOfShards() : num_tasks,
      base::BindOnce(
          [](ErrorCallback callback, const std::vector<Error>& errors) {
            // Return the first error, or kOk if all succeeded.
            for (const auto& error : errors) {
              if (error != Error::kOk) {
                std::move(callback).Run(error);
                return;
              }
            }
            std::move(callback).Run(Error::kOk);
          },
          std::move(callback)));
}

size_t SqlPersistentStore::GetSizeOfShards() const {
  return background_task_runners_.size();
}

SqlPersistentStore::BackendShard& SqlPersistentStore::GetShard(
    CacheEntryKey::Hash hash) const {
  return *backend_shards_[GetShardIdForHash(hash).value()];
}

SqlPersistentStore::BackendShard& SqlPersistentStore::GetShard(
    const CacheEntryKey& key) const {
  return GetShard(key.hash());
}

void SqlPersistentStore::OnInitializeFinished(
    ErrorCallback callback,
    std::vector<InitResultOrError> results) {
  CHECK_EQ(results.size(), GetSizeOfShards());
  int64_t total_database_size = 0;
  for (const auto& result : results) {
    if (!result.has_value()) {
      std::move(callback).Run(result.error());
      return;
    }
  }
  if (net::features::kSqlDiskCacheLoadIndexOnInit.Get()) {
    in_memory_load_result_ = Error::kOk;
  }
  for (const auto& result : results) {
    // Only the result from the shard 0 has max_bytes.
    if (result->max_bytes.has_value()) {
      SetMaxSize(*result->max_bytes);
      base::UmaHistogramMemoryLargeMB(
          base::StrCat({kSqlDiskCacheBackendHistogramPrefix, "MaxSize"}),
          *result->max_bytes / 1024 / 1024);
    }
    total_database_size += result->database_size;
  }
  base::UmaHistogramMemoryLargeMB(
      base::StrCat({kSqlDiskCacheBackendHistogramPrefix, "DatabaseSize"}),
      total_database_size / 1024 / 1024);
  base::UmaHistogramCounts1M(
      base::StrCat({kSqlDiskCacheBackendHistogramPrefix, "EntryCount"}),
      GetEntryCount());
  base::UmaHistogramMemoryLargeMB(
      base::StrCat({kSqlDiskCacheBackendHistogramPrefix, "TotalSize"}),
      GetSizeOfAllEntries() / 1024 / 1024);
  std::move(callback).Run(Error::kOk);
}

// Default constructor and move operations for EntryInfo.
SqlPersistentStore::EntryInfo::EntryInfo() = default;
SqlPersistentStore::EntryInfo::~EntryInfo() = default;
SqlPersistentStore::EntryInfo::EntryInfo(EntryInfo&&) = default;
SqlPersistentStore::EntryInfo& SqlPersistentStore::EntryInfo::operator=(
    EntryInfo&&) = default;

SqlPersistentStore::EntryMetadata::EntryMetadata(
    ResId res_id,
    base::Time last_used,
    std::optional<int64_t> bytes_usage)
    : res_id(res_id), last_used(last_used), bytes_usage(bytes_usage) {}
SqlPersistentStore::EntryMetadata::~EntryMetadata() = default;
SqlPersistentStore::EntryMetadata::EntryMetadata(const EntryMetadata&) =
    default;
SqlPersistentStore::EntryMetadata& SqlPersistentStore::EntryMetadata::operator=(
    const EntryMetadata&) = default;
SqlPersistentStore::EntryMetadata::EntryMetadata(EntryMetadata&&) = default;
SqlPersistentStore::EntryMetadata& SqlPersistentStore::EntryMetadata::operator=(
    EntryMetadata&&) = default;

SqlPersistentStore::ReadResult::ReadResult() = default;
SqlPersistentStore::ReadResult::~ReadResult() = default;
SqlPersistentStore::ReadResult::ReadResult(const ReadResult&) = default;
SqlPersistentStore::ReadResult& SqlPersistentStore::ReadResult::operator=(
    const ReadResult&) = default;
SqlPersistentStore::ReadResult::ReadResult(ReadResult&&) = default;
SqlPersistentStore::ReadResult& SqlPersistentStore::ReadResult::operator=(
    ReadResult&&) = default;

SqlPersistentStore::ResIdAndShardId::ResIdAndShardId(ResId res_id,
                                                     ShardId shard_id)
    : res_id(res_id), shard_id(shard_id) {}
SqlPersistentStore::ResIdAndShardId::ResIdAndShardId() = default;
SqlPersistentStore::ResIdAndShardId::~ResIdAndShardId() = default;
SqlPersistentStore::ResIdAndShardId::ResIdAndShardId(const ResIdAndShardId&) =
    default;
SqlPersistentStore::ResIdAndShardId&
SqlPersistentStore::ResIdAndShardId::operator=(const ResIdAndShardId&) =
    default;
SqlPersistentStore::ResIdAndShardId::ResIdAndShardId(ResIdAndShardId&&) =
    default;
SqlPersistentStore::ResIdAndShardId&
SqlPersistentStore::ResIdAndShardId::operator=(ResIdAndShardId&&) = default;

SqlPersistentStore::EntryInfoWithKeyAndIterator::EntryInfoWithKeyAndIterator() =
    default;
SqlPersistentStore::EntryInfoWithKeyAndIterator::
    ~EntryInfoWithKeyAndIterator() = default;
SqlPersistentStore::EntryInfoWithKeyAndIterator::EntryInfoWithKeyAndIterator(
    EntryInfoWithKeyAndIterator&&) = default;
SqlPersistentStore::EntryInfoWithKeyAndIterator&
SqlPersistentStore::EntryInfoWithKeyAndIterator::operator=(
    EntryInfoWithKeyAndIterator&&) = default;

int64_t SqlPersistentStore::StoreStatus::GetEstimatedDiskUsage() const {
  base::ClampedNumeric<int64_t> result;
  result = entry_count;
  result *= kSqlBackendStaticResourceSize;
  result += total_size;
  return result;
}

SqlPersistentStore::InMemoryIndexAndDoomedResIds::InMemoryIndexAndDoomedResIds(
    SqlPersistentStoreInMemoryIndex&& index,
    ResIdList doomed_entry_res_ids)
    : index(std::move(index)),
      doomed_entry_res_ids(std::move(doomed_entry_res_ids)) {}
SqlPersistentStore::InMemoryIndexAndDoomedResIds::
    ~InMemoryIndexAndDoomedResIds() = default;
SqlPersistentStore::InMemoryIndexAndDoomedResIds::InMemoryIndexAndDoomedResIds(
    InMemoryIndexAndDoomedResIds&& other) = default;
SqlPersistentStore::InMemoryIndexAndDoomedResIds&
SqlPersistentStore::InMemoryIndexAndDoomedResIds::operator=(
    InMemoryIndexAndDoomedResIds&& other) = default;

SqlPersistentStore::EvictionTarget::EvictionTarget(
    SqlPersistentStore::ResId res_id,
    int64_t entry_size_with_overhead)
    : res_id(res_id), entry_size_with_overhead(entry_size_with_overhead) {}
SqlPersistentStore::EvictionTarget::~EvictionTarget() = default;
SqlPersistentStore::EvictionTarget::EvictionTarget(EvictionTarget&&) = default;
SqlPersistentStore::EvictionTarget&
SqlPersistentStore::EvictionTarget::operator=(EvictionTarget&&) = default;
SqlPersistentStore::EvictionTarget::EvictionTarget(const EvictionTarget&) =
    default;
SqlPersistentStore::EvictionTarget&
SqlPersistentStore::EvictionTarget::operator=(const EvictionTarget&) = default;
bool SqlPersistentStore::EvictionTarget::operator==(
    const EvictionTarget& other) const = default;

SqlPersistentStore::EvictionResult::EvictionResult(
    Error error,
    size_t evicted_entry_count,
    std::vector<SqlSharedCacheResourceId> deleted_shared_cache_resources)
    : error(error),
      evicted_entry_count(evicted_entry_count),
      deleted_shared_cache_resources(
          std::move(deleted_shared_cache_resources)) {}
SqlPersistentStore::EvictionResult::~EvictionResult() = default;
SqlPersistentStore::EvictionResult::EvictionResult(EvictionResult&&) = default;
SqlPersistentStore::EvictionResult&
SqlPersistentStore::EvictionResult::operator=(EvictionResult&&) = default;

SqlPersistentStore::EvictionResultWithMetadata::EvictionResultWithMetadata(
    EvictionResult result,
    EvictionTargetQueue pending_eviction_targets,
    std::optional<SqlPersistentStoreInMemoryIndex> index,
    StoreStatus store_status,
    bool index_mismatch_detected)
    : result(std::move(result)),
      pending_eviction_targets(std::move(pending_eviction_targets)),
      index(std::move(index)),
      store_status(std::move(store_status)),
      index_mismatch_detected(index_mismatch_detected) {}
SqlPersistentStore::EvictionResultWithMetadata::~EvictionResultWithMetadata() =
    default;
SqlPersistentStore::EvictionResultWithMetadata::EvictionResultWithMetadata(
    EvictionResultWithMetadata&&) = default;
SqlPersistentStore::EvictionResultWithMetadata&
SqlPersistentStore::EvictionResultWithMetadata::operator=(
    EvictionResultWithMetadata&&) = default;

SqlPersistentStore::InitResult::InitResult(
    std::optional<int64_t> max_bytes,
    const StoreStatus& store_status,
    int64_t database_size,
    std::optional<InMemoryIndexAndDoomedResIds> in_memory_data)
    : max_bytes(max_bytes),
      store_status(store_status),
      database_size(database_size),
      in_memory_data(std::move(in_memory_data)) {}
SqlPersistentStore::InitResult::~InitResult() = default;
SqlPersistentStore::InitResult::InitResult(InitResult&& other) = default;
SqlPersistentStore::InitResult& SqlPersistentStore::InitResult::operator=(
    InitResult&& other) = default;

}  // namespace disk_cache
