blob: a003d394696285c5e5060b887d1a08e8782bf978 [file] [log] [blame]
// Copyright 2018 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_OPTIMIZATION_GUIDE_HINT_CACHE_H_
#define COMPONENTS_OPTIMIZATION_GUIDE_HINT_CACHE_H_
#include <string>
#include "base/callback.h"
#include "base/containers/mru_cache.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/time/clock.h"
#include "components/optimization_guide/memory_hint.h"
#include "components/optimization_guide/optimization_guide_store.h"
#include "components/optimization_guide/proto/hints.pb.h"
class GURL;
namespace optimization_guide {
class StoreUpdateData;
using HintLoadedCallback = base::OnceCallback<void(const proto::Hint*)>;
// Contains a set of optimization hints received from the Cacao service. This
// may include hints received from the ComponentUpdater and hints fetched from a
// Cacao Optimization Guide Service API. The availability of hints is queryable
// via host name and full URL. The cache itself consists of a backing store,
// which allows for asynchronous loading of any available host-keyed hint, and
// an MRU host-keyed cache and a url-keyed cache, which can be used to
// synchronously retrieve recently loaded hints keyed by URL or host.
class HintCache {
public:
// Construct the HintCache with an optional backing store and max host-keyed
// cache size. If a backing store is not provided, all hints will only be
// stored in-memory.
explicit HintCache(
std::unique_ptr<OptimizationGuideStore> optimization_guide_store,
int max_host_keyed_memory_cache_size);
~HintCache();
// Initializes the backing store contained within the hint cache, if provided,
// and asynchronously runs the callback after initialization is complete. If
// |purge_existing_data| is set to true, then the cache will purge any
// pre-existing data and begin in a clean state.
void Initialize(bool purge_existing_data, base::OnceClosure callback);
// Returns a StoreUpdateData. During component processing, hints from the
// component are moved into the StoreUpdateData. After component
// processing completes, the component update data is provided to the backing
// store in UpdateComponentHints() and used to update its component hints. In
// the case the provided component version is not newer than the store's
// version, nullptr will be returned by the call.
std::unique_ptr<StoreUpdateData> MaybeCreateUpdateDataForComponentHints(
const base::Version& version) const;
// Returns an UpdateData created by the store to hold updates for fetched
// hints. No version is needed nor applicable for fetched hints. During
// processing of the GetHintsResponse, hints are moved into the update data.
// After processing is complete, the update data is provided to the backing
// store to update hints. |update_time| specifies when the hints within the
// created update data will be scheduled to be updated.
std::unique_ptr<StoreUpdateData> CreateUpdateDataForFetchedHints(
base::Time update_time) const;
// Updates the store's component data using the provided StoreUpdateData
// and asynchronously runs the provided callback after the update finishes.
void UpdateComponentHints(std::unique_ptr<StoreUpdateData> component_data,
base::OnceClosure callback);
// Process |get_hints_response| to be stored in the hint cache store.
// |callback| is asynchronously run when the hints are successfully stored or
// if the store is not available. |update_time| specifies when the hints
// within |get_hints_response| will need to be updated next. |hosts_fetched|
// and |urls_fetched| specifies the hosts and URLs for which specific hints
// were requested to be fetched. It is expected for |this| to keep track of
// the result, even if a hint was not returned for the URL.
void UpdateFetchedHints(
std::unique_ptr<proto::GetHintsResponse> get_hints_response,
base::Time update_time,
const base::flat_set<std::string>& hosts_fetched,
const base::flat_set<GURL>& urls_fetched,
base::OnceClosure callback);
// Purges fetched hints from the owned |optimization_guide_store| that have
// expired.
void PurgeExpiredFetchedHints();
// Purges fetched hints from the owned |optimization_guide_store_| and resets
// both in-memory hint caches.
void ClearFetchedHints();
// Purges fetched hints from the owned |optimization_guide_store_| and resets
// the host-keyed cache.
void ClearHostKeyedHints();
// Returns whether the cache has a hint data for |host| locally (whether
// in the host-keyed cache or persisted on disk).
bool HasHint(const std::string& host);
// Requests that hint data for |host| be loaded asynchronously and passed to
// |callback| if/when loaded.
void LoadHint(const std::string& host, HintLoadedCallback callback);
// Returns the update time provided by |hint_store_|, which specifies when the
// fetched hints within the store are ready to be updated. If |hint_store_| is
// not initialized, base::Time() is returned.
base::Time GetFetchedHintsUpdateTime() const;
// Returns the hint data for |host| if found in the host-keyed cache,
// otherwise nullptr.
const proto::Hint* GetHostKeyedHintIfLoaded(const std::string& host);
// Returns an unepxired hint data for |url| if found in the url-keyed cache,
// otherwise nullptr. If the hint is expired, it is removed from the URL-keyed
// cache. Only HTTP/HTTPS URLs without username and password are supported by
// the URL-keyed cache, if |url| does not meet this criteria, nullptr is
// returned. The returned hint is not guaranteed to remain valid and will
// become invalid if any additional URL-keyed hints are added to the cache
// that evicts the returned hint.
proto::Hint* GetURLKeyedHint(const GURL& url);
// Returns true if the url-keyed cache contains an entry for |url|, even if
// the entry is empty. If a hint exists but is expired, it returns false.
bool HasURLKeyedEntryForURL(const GURL& url);
// Verifies and processes |hints| and moves the ones it supports into
// |update_data| and caches any valid URL keyed hints.
//
// Returns true if there was at least one hint is moved into |update_data|.
bool ProcessAndCacheHints(
google::protobuf::RepeatedPtrField<proto::Hint>* hints,
StoreUpdateData* update_data);
// Returns whether the persistent hint store owned by this is available.
bool IsHintStoreAvailable() const;
// Override |clock_| for testing.
void SetClockForTesting(const base::Clock* clock);
// Add hint to the URL-keyed cache. For testing only.
void AddHintForTesting(const GURL& gurl, std::unique_ptr<proto::Hint> hint);
private:
using HostKeyedHintCache =
base::HashingMRUCache<std::string, std::unique_ptr<MemoryHint>>;
using URLKeyedHintCache =
base::HashingMRUCache<std::string, std::unique_ptr<MemoryHint>>;
// The callback run after the store finishes initialization. This then runs
// the callback initially provided by the Initialize() call.
void OnStoreInitialized(base::OnceClosure callback);
// The callback run after the store finishes loading a hint. This adds the
// loaded hint to |host_keyed_cache_|, potentially purging the least recently
// used element, and then runs the callback initially provided by the
// LoadHint() call.
void OnLoadStoreHint(
const std::string& host,
HintLoadedCallback callback,
const OptimizationGuideStore::EntryKey& store_hint_entry_key,
std::unique_ptr<MemoryHint> hint);
// The backing store used with this hint cache. Set during construction.
const std::unique_ptr<OptimizationGuideStore> optimization_guide_store_;
// The cache of host-keyed hints loaded from the store. Maps store
// EntryKey to Hint proto. This serves two purposes:
// 1. Allows hints to be requested on navigation and retained in memory until
// commit, when they can be synchronously retrieved from the cache.
// 2. Reduces churn of needing to reload hints from frequently visited sites
// multiple times during a session.
HostKeyedHintCache host_keyed_cache_;
// The in-memory cache of URL-keyed hints fetched from the remote optimization
// guide. Maps a full URL to Hint proto. All available URL-keyed hints are
// maintained within the cache and are not persisted to disk.
URLKeyedHintCache url_keyed_hint_cache_;
// The clock used to determine if hints have expired.
const base::Clock* clock_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(HintCache);
};
} // namespace optimization_guide
#endif // COMPONENTS_OPTIMIZATION_GUIDE_HINT_CACHE_H_