request_throttler.cc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. // Copyright 2016 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. #include "components/ntp_snippets/remote/request_throttler.h"
  5. #include <climits>
  6. #include <set>
  7. #include "base/logging.h"
  8. #include "base/metrics/histogram.h"
  9. #include "base/strings/string_number_conversions.h"
  10. #include "base/strings/string_split.h"
  11. #include "base/strings/stringprintf.h"
  12. #include "base/time/time.h"
  13. #include "components/ntp_snippets/features.h"
  14. #include "components/ntp_snippets/ntp_snippets_constants.h"
  15. #include "components/ntp_snippets/pref_names.h"
  16. #include "components/prefs/pref_registry_simple.h"
  17. #include "components/prefs/pref_service.h"
  18. #include "components/variations/variations_associated_data.h"
  19. namespace ntp_snippets {
  20. namespace {
  21. // Enumeration listing all possible outcomes for fetch attempts. Used for UMA
  22. // histogram, so do not change existing values. Insert new values at the end,
  23. // and update the histogram definition.
  24. enum class RequestStatus {
  25. INTERACTIVE_QUOTA_GRANTED,
  26. BACKGROUND_QUOTA_GRANTED,
  27. BACKGROUND_QUOTA_EXCEEDED,
  28. INTERACTIVE_QUOTA_EXCEEDED,
  29. REQUEST_STATUS_COUNT
  30. };
  31. // Quota value to use if no quota should be applied (by default).
  32. const int kUnlimitedQuota = INT_MAX;
  33. } // namespace
  34. struct RequestThrottler::RequestTypeInfo {
  35. const char* name;
  36. const char* count_pref;
  37. const char* interactive_count_pref;
  38. const char* day_pref;
  39. const int default_quota;
  40. const int default_interactive_quota;
  41. };
  42. // When adding a new type here, extend also the "RequestThrottlerTypes"
  43. // <histogram_suffixes> in histograms.xml with the |name| string.
  44. const RequestThrottler::RequestTypeInfo RequestThrottler::kRequestTypeInfo[] = {
  45. // The following three types share the same prefs. They differ in quota
  46. // values (and UMA histograms).
  47. // RequestCounter::RequestType::CONTENT_SUGGESTION_FETCHER_RARE_NTP_USER,
  48. {"SuggestionFetcherRareNTPUser", prefs::kSnippetFetcherRequestCount,
  49. prefs::kSnippetFetcherInteractiveRequestCount,
  50. prefs::kSnippetFetcherRequestsDay, 5, kUnlimitedQuota},
  51. // RequestCounter::RequestType::CONTENT_SUGGESTION_FETCHER_ACTIVE_NTP_USER,
  52. {"SuggestionFetcherActiveNTPUser", prefs::kSnippetFetcherRequestCount,
  53. prefs::kSnippetFetcherInteractiveRequestCount,
  54. prefs::kSnippetFetcherRequestsDay, 20, kUnlimitedQuota},
  55. // RequestCounter::RequestType::CONTENT_SUGGESTION_FETCHER_ACTIVE_SUGGESTIONS_CONSUMER,
  56. {"SuggestionFetcherActiveSuggestionsConsumer",
  57. prefs::kSnippetFetcherRequestCount,
  58. prefs::kSnippetFetcherInteractiveRequestCount,
  59. prefs::kSnippetFetcherRequestsDay, 20, kUnlimitedQuota},
  60. // RequestCounter::RequestType::CONTENT_SUGGESTION_THUMBNAIL,
  61. {"SuggestionThumbnailFetcher", prefs::kSnippetThumbnailsRequestCount,
  62. prefs::kSnippetThumbnailsInteractiveRequestCount,
  63. prefs::kSnippetThumbnailsRequestsDay, kUnlimitedQuota, kUnlimitedQuota}};
  64. RequestThrottler::RequestThrottler(PrefService* pref_service, RequestType type)
  65. : pref_service_(pref_service),
  66. type_info_(kRequestTypeInfo[static_cast<int>(type)]) {
  67. DCHECK(pref_service);
  68. std::string quota = variations::GetVariationParamValueByFeature(
  69. ntp_snippets::kArticleSuggestionsFeature,
  70. base::StringPrintf("quota_%s", GetRequestTypeName()));
  71. if (!base::StringToInt(quota, &quota_)) {
  72. LOG_IF(WARNING, !quota.empty())
  73. << "Invalid variation parameter for quota for " << GetRequestTypeName();
  74. quota_ = type_info_.default_quota;
  75. }
  76. std::string interactive_quota = variations::GetVariationParamValueByFeature(
  77. ntp_snippets::kArticleSuggestionsFeature,
  78. base::StringPrintf("interactive_quota_%s", GetRequestTypeName()));
  79. if (!base::StringToInt(interactive_quota, &interactive_quota_)) {
  80. LOG_IF(WARNING, !interactive_quota.empty())
  81. << "Invalid variation parameter for interactive quota for "
  82. << GetRequestTypeName();
  83. interactive_quota_ = type_info_.default_interactive_quota;
  84. }
  85. // Since the histogram names are dynamic, we cannot use the standard macros
  86. // and we need to lookup the histograms, instead.
  87. int status_count = static_cast<int>(RequestStatus::REQUEST_STATUS_COUNT);
  88. // Corresponds to UMA_HISTOGRAM_ENUMERATION(name, sample, |status_count|).
  89. histogram_request_status_ = base::LinearHistogram::FactoryGet(
  90. base::StringPrintf("NewTabPage.RequestThrottler.RequestStatus_%s",
  91. GetRequestTypeName()),
  92. 1, status_count, status_count + 1,
  93. base::HistogramBase::kUmaTargetedHistogramFlag);
  94. // Corresponds to UMA_HISTOGRAM_COUNTS_100(name, sample).
  95. histogram_per_day_background_ = base::Histogram::FactoryGet(
  96. base::StringPrintf("NewTabPage.RequestThrottler.PerDay_%s",
  97. GetRequestTypeName()),
  98. 1, 100, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
  99. // Corresponds to UMA_HISTOGRAM_COUNTS_100(name, sample).
  100. histogram_per_day_interactive_ = base::Histogram::FactoryGet(
  101. base::StringPrintf("NewTabPage.RequestThrottler.PerDayInteractive_%s",
  102. GetRequestTypeName()),
  103. 1, 100, 50, base::HistogramBase::kUmaTargetedHistogramFlag);
  104. }
  105. // static
  106. void RequestThrottler::RegisterProfilePrefs(PrefRegistrySimple* registry) {
  107. // Collect all pref keys in a set to make sure we register each key exactly
  108. // once, even if they repeat.
  109. std::set<std::string> keys_to_register;
  110. for (const RequestTypeInfo& info : kRequestTypeInfo) {
  111. keys_to_register.insert(info.day_pref);
  112. keys_to_register.insert(info.count_pref);
  113. keys_to_register.insert(info.interactive_count_pref);
  114. }
  115. for (const std::string& key : keys_to_register) {
  116. registry->RegisterIntegerPref(key, 0);
  117. }
  118. }
  119. bool RequestThrottler::DemandQuotaForRequest(bool interactive_request) {
  120. ResetCounterIfDayChanged();
  121. int new_count = GetCount(interactive_request) + 1;
  122. SetCount(interactive_request, new_count);
  123. bool available = (new_count <= GetQuota(interactive_request));
  124. if (interactive_request) {
  125. histogram_request_status_->Add(static_cast<int>(
  126. available ? RequestStatus::INTERACTIVE_QUOTA_GRANTED
  127. : RequestStatus::INTERACTIVE_QUOTA_EXCEEDED));
  128. } else {
  129. histogram_request_status_->Add(
  130. static_cast<int>(available ? RequestStatus::BACKGROUND_QUOTA_GRANTED
  131. : RequestStatus::BACKGROUND_QUOTA_EXCEEDED));
  132. }
  133. return available;
  134. }
  135. void RequestThrottler::ResetCounterIfDayChanged() {
  136. // Get the date, "concatenated" into an int in "YYYYMMDD" format.
  137. base::Time::Exploded now_exploded{};
  138. base::Time::Now().LocalExplode(&now_exploded);
  139. int now_day = 10000 * now_exploded.year + 100 * now_exploded.month +
  140. now_exploded.day_of_month;
  141. if (!HasDay()) {
  142. // The counter is used for the first time in this profile.
  143. SetDay(now_day);
  144. } else if (now_day != GetDay()) {
  145. // Day has changed - report the number of requests from the previous day.
  146. histogram_per_day_background_->Add(GetCount(/*interactive_request=*/false));
  147. histogram_per_day_interactive_->Add(GetCount(/*interactive_request=*/true));
  148. // Reset the counters.
  149. SetCount(/*interactive_request=*/false, 0);
  150. SetCount(/*interactive_request=*/true, 0);
  151. SetDay(now_day);
  152. }
  153. }
  154. const char* RequestThrottler::GetRequestTypeName() const {
  155. return type_info_.name;
  156. }
  157. // TODO(jkrcal): turn RequestTypeInfo into a proper class, move those methods
  158. // onto the class and hide the members.
  159. int RequestThrottler::GetQuota(bool interactive_request) const {
  160. return interactive_request ? interactive_quota_ : quota_;
  161. }
  162. int RequestThrottler::GetCount(bool interactive_request) const {
  163. return pref_service_->GetInteger(interactive_request
  164. ? type_info_.interactive_count_pref
  165. : type_info_.count_pref);
  166. }
  167. void RequestThrottler::SetCount(bool interactive_request, int count) {
  168. pref_service_->SetInteger(interactive_request
  169. ? type_info_.interactive_count_pref
  170. : type_info_.count_pref,
  171. count);
  172. }
  173. int RequestThrottler::GetDay() const {
  174. return pref_service_->GetInteger(type_info_.day_pref);
  175. }
  176. void RequestThrottler::SetDay(int day) {
  177. pref_service_->SetInteger(type_info_.day_pref, day);
  178. }
  179. bool RequestThrottler::HasDay() const {
  180. return pref_service_->HasPrefPath(type_info_.day_pref);
  181. }
  182. } // namespace ntp_snippets