blob: aaad51e7adc618837fd9561f975d49f95dd8741c [file] [log] [blame]
// 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_HTTP_NO_VARY_SEARCH_CACHE_STORAGE_H_
#define NET_HTTP_NO_VARY_SEARCH_CACHE_STORAGE_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <optional>
#include <string>
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "net/base/net_export.h"
#include "net/http/no_vary_search_cache.h"
namespace net {
class NoVarySearchCache;
class NoVarySearchCacheStorageFileOperations;
// An implementation of persistence for NoVarySearchCache. Disk operations are
// performed in the background on a thread pool.
class NET_EXPORT_PRIVATE NoVarySearchCacheStorage final
: public NoVarySearchCache::Journal {
public:
enum class LoadFailed { kCannotJournal };
using LoadResult =
base::expected<std::unique_ptr<NoVarySearchCache>, LoadFailed>;
using LoadCallback = base::OnceCallback<void(LoadResult)>;
// Exposed for testing.
static constexpr std::string_view kSnapshotFilename = "snapshot.baf";
static constexpr std::string_view kJournalFilename = "journal.baj";
// Generated by the command:
// echo "snapshot.baf version 2" | md5sum | cut -b 1-8
static constexpr uint32_t kSnapshotMagicNumber = 0x62f965ce;
// Generated by the command:
// echo "journal.baj version 2" | md5sum | cut -b 1-8
static constexpr uint32_t kJournalMagicNumber = 0x24467e06;
// Max file size. We won't try to load a file larger than this. We don't
// expect the database to ever get this big.
static constexpr size_t kMaxFileSize = 100 * 1024 * 1024;
// Avoid automatically taking a new snapshot until the journal gets this big,
// even if the snapshot is smaller.
static constexpr size_t kMinimumAutoSnapshotSize = 64 * 1024;
// Create a NoVarySearchCacheStorage object to persist `cache`. Load() must be
// called in order for it to actually do anything.
NoVarySearchCacheStorage();
NoVarySearchCacheStorage(const NoVarySearchCacheStorage&) = delete;
NoVarySearchCacheStorage& operator=(const NoVarySearchCacheStorage&) = delete;
~NoVarySearchCacheStorage() override;
// Asynchronously loads a previously persisted NoVarySearchCache object using
// `file_operations`, then starts journalling to a new journal file. If an
// existing persisted cache could not be loaded, creates an empty
// NoVarySearchCache object with `max_size` configured to be
// `default_max_size`. If a new journal file could not be created, `callback`
// is called with base::unexpected(kCannotJournal). Otherwise it is called
// with a NoVarySearchCache object wrapped in a std::unique_ptr. `callback`
// should merge in any entries that were added to the temporary cache used
// during loading, and then switch to using the new cache. Any inserts or
// erasures performed on the new cache will be journalled to disk. The new
// NoVarySearchCache object should not be destroyed before this object is.
// `callback` is never called synchronously.
void Load(
std::unique_ptr<NoVarySearchCacheStorageFileOperations> file_operations,
size_t default_max_size,
LoadCallback callback);
// Serializes and persists the current state of `cache_` to disk, and starts a
// new empty journal. Does nothing if the initial load hasn't completed yet.
// This method is also called automatically any time the journal file grows
// too large.
void TakeSnapshot();
// Implementation of NoVarySearchCache::Journal.
// Serializes the arguments and posts a task to `journal_` to append the
// result to the journal.
void OnInsert(const std::string& partition_key,
const std::string& base_url,
const HttpNoVarySearchData& nvs_data,
const std::optional<std::string>& query,
base::Time update_time) override;
// Serializes the arguments and posts a task to `journal_` to append to the
// journal.
void OnErase(const std::string& partition_key,
const std::string& base_url,
const HttpNoVarySearchData& nvs_data,
const std::optional<std::string>& query) override;
bool IsJournallingForTesting() const { return static_cast<bool>(journal_); }
private:
class Journaller;
class Loader;
using JournallerPtr = std::unique_ptr<Journaller, base::OnTaskRunnerDeleter>;
// On successful creation of the Journaller on the background thread, a
// pointer to it needs to be passed back to the
// NoVarySearchCacheStorage::OnLoadComplete() method so that it can be called
// and have its lifetime managed. We also need the loaded NoVarySearchCache
// object.
struct CacheAndJournalPointers {
CacheAndJournalPointers(std::unique_ptr<NoVarySearchCache> cache,
JournallerPtr journal);
CacheAndJournalPointers(const CacheAndJournalPointers&) = delete;
// Move construction is allowed.
CacheAndJournalPointers(CacheAndJournalPointers&&);
CacheAndJournalPointers& operator=(const CacheAndJournalPointers&) = delete;
CacheAndJournalPointers& operator=(CacheAndJournalPointers&&) = delete;
~CacheAndJournalPointers();
std::unique_ptr<NoVarySearchCache> cache;
JournallerPtr journal;
};
// Called when loading the persisted NoVarySearchCache has succeeded or
// failed. Sets `cache_`, registers this object as a Journal for it, and
// assigns to `journal_` on success. Calls `callback`.
void OnLoadComplete(
LoadCallback callback,
base::expected<CacheAndJournalPointers, LoadFailed> result);
// Called when `journal_` has found that journalling failed. Clears `journal_`
// and unregisters this object as an observer.
void OnJournallingFailed();
// Appends `pickle` to the journal file.
void AppendToJournal(base::Pickle pickle);
raw_ptr<NoVarySearchCache> cache_ = nullptr;
// Task runner for file operations.
scoped_refptr<base::SequencedTaskRunner> background_task_runner_;
// `journal_` lives on the `background_task_runner_`.
JournallerPtr journal_;
// Time when Start() was called.
base::Time start_time_;
base::WeakPtrFactory<NoVarySearchCacheStorage> weak_factory_{this};
};
} // namespace net
#endif // NET_HTTP_NO_VARY_SEARCH_CACHE_STORAGE_H_