history_clusters_service.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_SERVICE_H_
  5. #define COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_SERVICE_H_
  6. #include <map>
  7. #include <memory>
  8. #include <string>
  9. #include <unordered_set>
  10. #include <vector>
  11. #include "base/callback.h"
  12. #include "base/memory/scoped_refptr.h"
  13. #include "base/memory/weak_ptr.h"
  14. #include "base/observer_list.h"
  15. #include "base/scoped_observation.h"
  16. #include "base/supports_user_data.h"
  17. #include "base/task/cancelable_task_tracker.h"
  18. #include "base/time/time.h"
  19. #include "base/timer/elapsed_timer.h"
  20. #include "base/timer/timer.h"
  21. #include "components/history/core/browser/history_service.h"
  22. #include "components/history/core/browser/history_service_observer.h"
  23. #include "components/history/core/browser/history_types.h"
  24. #include "components/history_clusters/core/clustering_backend.h"
  25. #include "components/history_clusters/core/history_clusters_service_task_get_most_recent_clusters.h"
  26. #include "components/history_clusters/core/history_clusters_service_task_update_clusters.h"
  27. #include "components/history_clusters/core/history_clusters_types.h"
  28. #include "components/keyed_service/core/keyed_service.h"
  29. #include "services/network/public/cpp/shared_url_loader_factory.h"
  30. namespace optimization_guide {
  31. class EntityMetadataProvider;
  32. class NewOptimizationGuideDecider;
  33. } // namespace optimization_guide
  34. namespace site_engagement {
  35. class SiteEngagementScoreProvider;
  36. } // namespace site_engagement
  37. namespace history_clusters {
  38. class HistoryClustersService;
  39. // Clears `HistoryClustersService`'s keyword cache when 1 or more history
  40. // entries are deleted.
  41. class VisitDeletionObserver : public history::HistoryServiceObserver {
  42. public:
  43. explicit VisitDeletionObserver(
  44. HistoryClustersService* history_clusters_service);
  45. ~VisitDeletionObserver() override;
  46. // Starts observing a service for history deletions.
  47. void AttachToHistoryService(history::HistoryService* history_service);
  48. // history::HistoryServiceObserver
  49. void OnURLsDeleted(history::HistoryService* history_service,
  50. const history::DeletionInfo& deletion_info) override;
  51. private:
  52. HistoryClustersService* history_clusters_service_;
  53. // Tracks the observed history service, for cleanup.
  54. base::ScopedObservation<history::HistoryService,
  55. history::HistoryServiceObserver>
  56. history_service_observation_{this};
  57. };
  58. // This Service provides an API to the History Clusters for UI entry points.
  59. class HistoryClustersService : public base::SupportsUserData,
  60. public KeyedService {
  61. public:
  62. class Observer : public base::CheckedObserver {
  63. public:
  64. virtual void OnDebugMessage(const std::string& message) = 0;
  65. };
  66. // Use std::unordered_map here because we have ~1000 elements at the 99th
  67. // percentile, and we do synchronous lookups as the user types in the omnibox.
  68. using KeywordMap =
  69. std::unordered_map<std::u16string, history::ClusterKeywordData>;
  70. using URLKeywordSet = std::unordered_set<std::string>;
  71. // `url_loader_factory` is allowed to be nullptr, like in unit tests.
  72. // In that case, HistoryClustersService will never instantiate a clustering
  73. // backend that requires it, such as the RemoteClusteringBackend.
  74. HistoryClustersService(
  75. const std::string& application_locale,
  76. history::HistoryService* history_service,
  77. optimization_guide::EntityMetadataProvider* entity_metadata_provider,
  78. scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
  79. site_engagement::SiteEngagementScoreProvider* engagement_score_provider,
  80. optimization_guide::NewOptimizationGuideDecider*
  81. optimization_guide_decider);
  82. HistoryClustersService(const HistoryClustersService&) = delete;
  83. HistoryClustersService& operator=(const HistoryClustersService&) = delete;
  84. ~HistoryClustersService() override;
  85. // Gets a weak pointer to this service. Used when UIs want to create a query
  86. // state object whose lifetime might exceed the service.
  87. base::WeakPtr<HistoryClustersService> GetWeakPtr();
  88. // KeyedService:
  89. void Shutdown() override;
  90. // Returns true if the Journeys feature is enabled for the current application
  91. // locale. This is a cached wrapper of `IsJourneysEnabled()` within features.h
  92. // that's already evaluated against the g_browser_process application locale.
  93. bool IsJourneysEnabled() const;
  94. // Used to add and remove observers.
  95. void AddObserver(Observer* obs);
  96. void RemoveObserver(Observer* obs);
  97. // Returns whether observers are registered to notify the debug messages.
  98. bool ShouldNotifyDebugMessage() const;
  99. // Notifies the observers of a debug message being available.
  100. void NotifyDebugMessage(const std::string& message) const;
  101. // TODO(manukh) `HistoryClustersService` should be responsible for
  102. // constructing the
  103. // `AnnotatedVisit`s rather than exposing these methods which are used by
  104. // `HistoryClustersTabHelper` to construct the visits.
  105. // Gets an `IncompleteVisitContextAnnotations` after DCHECKing it exists; this
  106. // saves the call sites the effort.
  107. IncompleteVisitContextAnnotations& GetIncompleteVisitContextAnnotations(
  108. int64_t nav_id);
  109. // Gets or creates an `IncompleteVisitContextAnnotations`.
  110. IncompleteVisitContextAnnotations&
  111. GetOrCreateIncompleteVisitContextAnnotations(int64_t nav_id);
  112. // Returns whether an `IncompleteVisitContextAnnotations` exists.
  113. // TODO(manukh): Merge `HasIncompleteVisitContextAnnotations()` and
  114. // `GetIncompleteVisitContextAnnotations()`.
  115. bool HasIncompleteVisitContextAnnotations(int64_t nav_id);
  116. // Completes the `IncompleteVisitContextAnnotations` if the expected metrics
  117. // have been recorded. References retrieved prior will no longer be valid.
  118. void CompleteVisitContextAnnotationsIfReady(int64_t nav_id);
  119. // This is a low-level API that doesn't support querying by search terms or
  120. // de-duplication across multiple batches. Any UI should almost certainly use
  121. // `QueryClustersState` instead.
  122. //
  123. // Entrypoint to `HistoryClustersServiceTaskGetMostRecentClusters`.
  124. //
  125. // Returns the freshest clusters created from the user visit history based on
  126. // `query`, `begin_time`, and `continuation_params`.
  127. // - `begin_time` is an inclusive lower bound. In the general case where the
  128. // caller wants to traverse to the start of history, `base::Time()` should
  129. // be used.
  130. // - `continuation_params` represents where the previous request left off. It
  131. // should be set to the default initialized
  132. // `QueryClustersContinuationParams`
  133. // if the caller wants the newest visits.
  134. // - `recluster`, if true, forces reclustering as if
  135. // `persist_clusters_in_history_db` were false.
  136. // The returned clusters are sorted in reverse-chronological order based on
  137. // their highest scoring visit. The visits within each cluster are sorted by
  138. // score, from highest to lowest.
  139. //
  140. // TODO(tommycli): Investigate entirely hiding access to this low-level method
  141. // behind QueryClustersState.
  142. std::unique_ptr<HistoryClustersServiceTaskGetMostRecentClusters>
  143. QueryClusters(ClusteringRequestSource clustering_request_source,
  144. base::Time begin_time,
  145. QueryClustersContinuationParams continuation_params,
  146. bool recluster,
  147. QueryClustersCallback callback);
  148. // Invokes `UpdateClusters()` after a short delay, then again periodically.
  149. // E.g., might invoke `UpdateClusters()` initially 5 minutes after startup,
  150. // then every 1 hour afterwards.
  151. void RepeatedlyUpdateClusters();
  152. // Entrypoint to the `HistoryClustersServiceTaskUpdateClusters`. Updates the
  153. // persisted clusters in the history DB and invokes `callback` when done.
  154. void UpdateClusters();
  155. // Returns matched keyword data from cache synchronously if `query` matches a
  156. // cluster keyword. This ignores clusters with only one visit to avoid
  157. // overtriggering. Note: This depends on the cache state, so this may kick off
  158. // a cache refresh request while immediately returning null data. It's
  159. // expected that on the next keystroke, the cache may be ready and return the
  160. // matched keyword data then.
  161. absl::optional<history::ClusterKeywordData> DoesQueryMatchAnyCluster(
  162. const std::string& query);
  163. // Returns true if `url_keyword` matches a URL in a significant cluster. This
  164. // may kick off a cache refresh while still immediately returning false.
  165. // `url_keyword` is derived from a given URL by ComputeURLKeywordForLookup().
  166. // SRP URLs canonicalized by TemplateURLService should be passed in directly.
  167. bool DoesURLMatchAnyCluster(const std::string& url_keyword);
  168. // Clears `all_keywords_cache_` and cancels any pending tasks to populate it.
  169. void ClearKeywordCache();
  170. private:
  171. friend class HistoryClustersServiceTestApi;
  172. // Starts a keyword cache refresh, if necessary.
  173. void StartKeywordCacheRefresh();
  174. // This is a callback used for the `QueryClusters()` call from
  175. // `DoesQueryMatchAnyCluster()`. Accumulates the keywords in `result` within
  176. // `keyword_accumulator`. If History is not yet exhausted, will request
  177. // another batch of clusters. Otherwise, will update the keyword cache.
  178. void PopulateClusterKeywordCache(
  179. base::ElapsedTimer total_latency_timer,
  180. base::Time begin_time,
  181. std::unique_ptr<KeywordMap> keyword_accumulator,
  182. std::unique_ptr<URLKeywordSet> url_keyword_accumulator,
  183. KeywordMap* cache,
  184. URLKeywordSet* url_cache,
  185. std::vector<history::Cluster> clusters,
  186. QueryClustersContinuationParams continuation_params);
  187. // True if Journeys is enabled based on field trial and locale checks.
  188. const bool is_journeys_enabled_;
  189. // Non-owning pointer, but never nullptr.
  190. history::HistoryService* const history_service_;
  191. // `VisitContextAnnotations`s are constructed stepwise; they're initially
  192. // placed in `incomplete_visit_context_annotations_` and saved to the history
  193. // database once completed (if persistence is enabled).
  194. IncompleteVisitMap incomplete_visit_context_annotations_;
  195. // The backend used for clustering. This can be nullptr.
  196. std::unique_ptr<ClusteringBackend> backend_;
  197. // In-memory cache of keywords match clusters, so we can query this
  198. // synchronously as the user types in the omnibox. Also save the timestamp
  199. // the cache was generated so we can periodically re-generate.
  200. // TODO(tommycli): Make a smarter mechanism for regenerating the cache.
  201. KeywordMap all_keywords_cache_;
  202. URLKeywordSet all_url_keywords_cache_;
  203. base::Time all_keywords_cache_timestamp_;
  204. // Like above, but will represent the clusters newer than
  205. // `all_keywords_cache_timestamp_` I.e., this will contain up to 2 hours of
  206. // clusters. This can be up to 10 seconds stale. We use a separate cache that
  207. // can repeatedly be cleared and recreated instead of incrementally adding
  208. // keywords to `all_keywords_cache_` because doing the latter might:
  209. // 1) Give a different set of keywords since cluster keywords aren't
  210. // necessarily a union of the individual visits' keywords.
  211. // 2) Exclude keywords since keywords of size-1 clusters are not cached.
  212. // TODO(manukh) This is a "band aid" fix to missing keywords for recent
  213. // visits.
  214. KeywordMap short_keyword_cache_;
  215. URLKeywordSet short_url_keywords_cache_;
  216. base::Time short_keyword_cache_timestamp_;
  217. // Tracks the current keyword task. Will be `nullptr` or
  218. // `cache_keyword_query_task_.Done()` will be true if there is no ongoing
  219. // task.
  220. std::unique_ptr<HistoryClustersServiceTaskGetMostRecentClusters>
  221. cache_keyword_query_task_;
  222. // Tracks the current update task. Will be `nullptr` or
  223. // `update_clusters_task_.Done()` will be true if there is no ongoing task.
  224. std::unique_ptr<HistoryClustersServiceTaskUpdateClusters>
  225. update_clusters_task_;
  226. // Used to invoke `UpdateClusters()` on startup after a short delay. See
  227. // `RepeatedlyUpdateClusters()`'s comment.
  228. base::OneShotTimer update_clusters_after_startup_delay_timer_;
  229. // Used to invoke `UpdateClusters()` periodically. See
  230. // `RepeatedlyUpdateClusters()`'s comment.
  231. base::RepeatingTimer update_clusters_period_timer_;
  232. // A list of observers for this service.
  233. base::ObserverList<Observer> observers_;
  234. VisitDeletionObserver visit_deletion_observer_;
  235. // Weak pointers issued from this factory never get invalidated before the
  236. // service is destroyed.
  237. base::WeakPtrFactory<HistoryClustersService> weak_ptr_factory_{this};
  238. };
  239. } // namespace history_clusters
  240. #endif // COMPONENTS_HISTORY_CLUSTERS_CORE_HISTORY_CLUSTERS_SERVICE_H_