blob: e6e00b96a57f6180b1bf917bccb6a93dfb13708d [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_LEVELDB_STORE_H_
#define COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_LEVELDB_STORE_H_
#include "components/previews/content/hint_cache_store.h"
#include <map>
#include <string>
#include <unordered_set>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "components/leveldb_proto/public/proto_database.h"
#include "components/previews/content/proto/hint_cache.pb.h"
namespace base {
class SequencedTaskRunner;
} // namespace base
namespace previews {
// Concrete implementation of HintCacheStore using LevelDB. All public calls in
// the store must be made on the same thread.
class HintCacheLevelDBStore : public HintCacheStore {
public:
using StoreEntryProtoDatabase =
leveldb_proto::ProtoDatabase<previews::proto::StoreEntry>;
// Status of the store. The store begins in kUninitialized, transitions to
// kInitializing after Initialize() is called, and transitions to kAvailable
// if initialization successfully completes. In the case where anything fails,
// the store transitions to kFailed, at which point it is fully purged and
// becomes unusable.
//
// Keep in sync with PreviewsHintCacheLevelDBStoreStatus in
// tools/metrics/histograms/enums.xml.
enum class Status {
kUninitialized = 0,
kInitializing = 1,
kAvailable = 2,
kFailed = 3,
kMaxValue = kFailed,
};
HintCacheLevelDBStore(
const base::FilePath& database_dir,
scoped_refptr<base::SequencedTaskRunner> store_task_runner);
HintCacheLevelDBStore(const base::FilePath& database_dir,
std::unique_ptr<StoreEntryProtoDatabase> database);
~HintCacheLevelDBStore() override;
// HintCacheStore overrides:
void Initialize(bool purge_existing_data,
base::OnceClosure callback) override;
std::unique_ptr<ComponentUpdateData> MaybeCreateComponentUpdateData(
const base::Version& version) const override;
void UpdateComponentData(std::unique_ptr<ComponentUpdateData> component_data,
base::OnceClosure callback) override;
bool FindHintEntryKey(const std::string& host_suffix,
EntryKey* out_hint_entry_key) const override;
void LoadHint(const EntryKey& hint_entry_key,
HintLoadedCallback callback) override;
private:
friend class HintCacheLevelDBStoreTest;
using EntryKeyPrefix = std::string;
using EntryKeySet = std::unordered_set<EntryKey>;
using EntryVector =
leveldb_proto::ProtoDatabase<previews::proto::StoreEntry>::KeyEntryVector;
using EntryMap = std::map<EntryKey, previews::proto::StoreEntry>;
// Entry types within the store appear at the start of the keys of entries.
// These values are converted into strings within the key: a key starting with
// "1_" signifies a metadata entry and one starting with "2_" signifies a
// component hint entry. Adding this to the start of the key allows the store
// to quickly perform operations on all entries of a specific key type. Given
// that entry type comparisons may be performed many times, the entry type
// string is kept as small as possible.
// Example metadata entry type key:
// "[EntryType::kMetadata]_[MetadataType::kSchema]" ==> "1_1"
// Example component hint entry type key:
// "[EntryType::kComponentHint]_[component_version]_[host]"
// ==> "2_55_foo.com"
// NOTE: The order and value of the existing entry types within the enum
// cannot be changed, but new types can be added to the end.
enum class EntryType {
kMetadata = 1,
kComponentHint = 2,
};
// Metadata types within the store. The metadata type appears at the end of
// metadata entry keys. These values are converted into strings within the
// key.
// Example metadata type keys:
// "[EntryType::kMetadata]_[MetadataType::kSchema]" ==> "1_1"
// "[EntryType::kMetadata]_[MetadataType::kComponent]" ==> "1_2"
// NOTE: The order and value of the existing metadata types within the enum
// cannot be changed, but new types can be added to the end.
enum class MetadataType {
kSchema = 1,
kComponent = 2,
};
// HintCacheLevelDBStore's concrete implementation of ComponentUpdateData.
// LevelDBComponentUpdateData is private within HintCacheLevelDBStore. All
// classes outside of HintCacheLevelDBStore can only interact with the
// ComponentUpdateData base class. LevelDBComponentUpdateData is created by
// HintCacheLevelDBStore when MaybeCreateComponentUpdateData() is called and
// used to update the store's component data during UpdateComponentData().
class LevelDBComponentUpdateData : public ComponentUpdateData {
public:
explicit LevelDBComponentUpdateData(const base::Version& version);
~LevelDBComponentUpdateData() override;
// ComponentUpdateData overrides:
void MoveHintIntoUpdateData(
optimization_guide::proto::Hint&& hint) override;
private:
friend class HintCacheLevelDBStore;
// The prefix to add to the key of every component hint entry. It is set
// during construction, using the provided component version.
const EntryKeyPrefix component_hint_entry_key_prefix_;
// The vector of entries to save. This contains both the metadata component
// entry, which is created during construction, using the provided component
// version, and the hint entries from the component, which are individually
// moved into |entries_to_save_| during calls to MoveHintIntoUpdateData().
std::unique_ptr<EntryVector> entries_to_save_;
};
// Current schema version of the hint cache store. When this is changed,
// pre-existing store data from an earlier version is purged.
static const char kStoreSchemaVersion[];
// Returns prefix in the key of every metadata entry type entry: "1_"
static EntryKeyPrefix GetMetadataEntryKeyPrefix();
// Returns entry key for the specified metadata type entry: "1_[MetadataType]"
static EntryKey GetMetadataTypeEntryKey(MetadataType metadata_type);
// Returns prefix in the key of every component hint entry: "2_"
static EntryKeyPrefix GetComponentHintEntryKeyPrefixWithoutVersion();
// Returns prefix in the key of component hint entries for the specified
// component version: "2_[component_version]_"
static EntryKeyPrefix GetComponentHintEntryKeyPrefix(
const base::Version& component_version);
// Updates the status of the store to the specified value, validates the
// transition, and destroys the database in the case where the status
// transitions to Status::kFailed.
void UpdateStatus(Status new_status);
// Returns true if the current status is Status::kAvailable.
bool IsAvailable() const;
// Asynchronously purges all existing entries from the database and runs the
// callback after it completes. This should only be run during initialization.
void PurgeDatabase(base::OnceClosure callback);
// Updates |component_version_| and |component_hint_entry_key_prefix_| for
// the new component version. This must be called rather than directly
// modifying |component_version_|, as it ensures that both member variables
// are updated in sync.
void SetComponentVersion(const base::Version& component_version);
// Resets |component_version_| and |component_hint_entry_key_prefix_| back to
// their default state. Called after the database is destroyed.
void ClearComponentVersion();
// Asynchronously loads the hint entry keys from the store, populates
// |hint_entry_keys_| with them, and runs the provided callback after they
// finish loading. In the case where there is currently an in-flight component
// update, this does nothing, as the hint entry keys will be loaded after the
// component update completes.
void MaybeLoadHintEntryKeys(base::OnceClosure callback);
// Returns the total hint entry keys contained within the store.
size_t GetHintEntryKeyCount() const;
// Callback that runs after the database finishes being initialized. If
// |purge_existing_data| is true, then unconditionally purges the database;
// otherwise, triggers loading of the metadata.
void OnDatabaseInitialized(bool purge_existing_data,
base::OnceClosure callback,
bool success);
// Callback that is run after the database finishes being destroyed.
void OnDatabaseDestroyed(bool success);
// Callback that runs after the metadata finishes being loaded. This
// validates the schema version, sets the component version, and either purges
// the store (on a schema version mismatch) or loads all hint entry keys (on
// a schema version match).
void OnLoadMetadata(base::OnceClosure callback,
bool success,
std::unique_ptr<EntryMap> metadata_entries);
// Callback that runs after the database is purged during initialization.
void OnPurgeDatabase(base::OnceClosure callback, bool success);
// Callback that runs after the component data within the store is fully
// updated. If the update was successful, it attempts to load all of the hint
// entry keys contained within the database.
void OnUpdateComponentData(base::OnceClosure callback, bool success);
// Callback that runs after the hint entry keys are fully loaded. If there's
// currently an in-flight component update, then the hint entry keys will be
// loaded again after the component update completes, so the results are
// tossed; otherwise, |hint_entry_keys| is moved into |hint_entry_keys_|.
// Regardless of the outcome of loading the keys, the callback always runs.
void OnLoadHintEntryKeys(std::unique_ptr<EntryKeySet> hint_entry_keys,
base::OnceClosure callback,
bool success,
std::unique_ptr<EntryMap> unused);
// Callback that runs after a hint entry is loaded from the database. If
// there's currently an in-flight component update, then the hint is about to
// be invalidated, so results are tossed; otherwise, the hint is released into
// the callback, allowing the caller to own the hint without copying it.
// Regardless of the success or failure of retrieving the key, the callback
// always runs (it simply runs with a nullptr on failure).
void OnLoadHint(const EntryKey& entry_key,
HintLoadedCallback callback,
bool success,
std::unique_ptr<previews::proto::StoreEntry> entry);
// Path to the directory for the profile using this store.
const base::FilePath database_dir_;
// Proto database used by the store.
const std::unique_ptr<StoreEntryProtoDatabase> database_;
// The current status of the store. It should only be updated through
// UpdateStatus(), which validates status transitions and triggers
// accompanying logic.
Status status_;
// The current component version of the store. This should only be updated
// via SetComponentVersion(), which ensures that both |component_version_|
// and |component_hint_key_prefix_| are updated at the same time.
base::Optional<base::Version> component_version_;
// The current entry key prefix shared by all component hints containd within
// the store. While this could be generated on the fly using
// |component_version_|, it is retaind separately as an optimization, as it
// is needed often.
EntryKeyPrefix component_hint_entry_key_prefix_;
// If a component data update is in the middle of being processed; when this
// is true, keys and hints will not be returned by the store.
bool component_data_update_in_flight_;
// The keys of the hints available within the store.
std::unique_ptr<EntryKeySet> hint_entry_keys_;
SEQUENCE_CHECKER(sequence_checker_);
base::WeakPtrFactory<HintCacheLevelDBStore> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(HintCacheLevelDBStore);
};
} // namespace previews
#endif // COMPONENTS_PREVIEWS_CONTENT_HINT_CACHE_LEVELDB_STORE_H_