Avi Drissman | 8ba1bad | 2022-09-13 19:22:36 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Leonid Baraz | f10cae8 | 2021-09-14 00:59:38 | [diff] [blame] | 5 | #include "components/reporting/client/report_queue_impl.h" |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 6 | |
| 7 | #include <memory> |
Arthur Sonzogni | c571efb | 2024-01-26 20:26:18 | [diff] [blame] | 8 | #include <optional> |
Leonid Baraz | f84aa6c | 2021-12-13 19:38:20 | [diff] [blame] | 9 | #include <queue> |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 10 | #include <string> |
| 11 | #include <utility> |
| 12 | |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 13 | #include "base/check.h" |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 14 | #include "base/check_op.h" |
Avi Drissman | 12be031 | 2023-01-11 09:16:09 | [diff] [blame] | 15 | #include "base/functional/bind.h" |
| 16 | #include "base/functional/callback.h" |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 17 | #include "base/location.h" |
Hong Xu | 353b7355 | 2023-10-27 19:43:50 | [diff] [blame] | 18 | #include "base/logging.h" |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 19 | #include "base/memory/ptr_util.h" |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 20 | #include "base/memory/scoped_refptr.h" |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 21 | #include "base/memory/weak_ptr.h" |
Hong Xu | e8406051 | 2023-01-31 10:26:18 | [diff] [blame] | 22 | #include "base/metrics/histogram_functions.h" |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 23 | #include "base/notreached.h" |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 24 | #include "base/sequence_checker.h" |
Hong Xu | e8406051 | 2023-01-31 10:26:18 | [diff] [blame] | 25 | #include "base/strings/strcat.h" |
| 26 | #include "base/strings/string_number_conversions.h" |
Patrick Monette | 643cdf6 | 2021-10-15 19:13:42 | [diff] [blame] | 27 | #include "base/task/bind_post_task.h" |
Sean Maher | e672a66 | 2023-01-09 21:42:28 | [diff] [blame] | 28 | #include "base/task/sequenced_task_runner.h" |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 29 | #include "base/task/task_traits.h" |
| 30 | #include "base/task/thread_pool.h" |
| 31 | #include "base/time/time.h" |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 32 | #include "base/types/expected.h" |
Leonid Baraz | caac7c9 | 2021-03-04 17:34:05 | [diff] [blame] | 33 | #include "components/reporting/client/report_queue_configuration.h" |
Josh Hilke | 1921032 | 2023-09-19 19:26:35 | [diff] [blame] | 34 | #include "components/reporting/proto/synced/metric_data.pb.h" |
Josh Hilke | e7a4699 | 2021-10-21 20:21:19 | [diff] [blame] | 35 | #include "components/reporting/proto/synced/record.pb.h" |
| 36 | #include "components/reporting/proto/synced/record_constants.pb.h" |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 37 | #include "components/reporting/storage/storage_module_interface.h" |
Josh Hilke | 1c36a73 | 2024-05-22 22:38:20 | [diff] [blame] | 38 | #include "components/reporting/util/reporting_errors.h" |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 39 | #include "components/reporting/util/status.h" |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 40 | #include "components/reporting/util/statusor.h" |
| 41 | |
| 42 | namespace reporting { |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 43 | namespace { |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 44 | |
Hong Xu | e8406051 | 2023-01-31 10:26:18 | [diff] [blame] | 45 | // UTC time of 2122-01-01T00:00:00Z since Unix epoch 1970-01-01T00:00:00Z in |
| 46 | // microseconds. |
| 47 | constexpr int64_t kTime2122 = 4'796'668'800'000'000; |
| 48 | |
Josh Hilke | 1921032 | 2023-09-19 19:26:35 | [diff] [blame] | 49 | // Returns true if record is allowed to go to `destination`. Returns false |
| 50 | // otherwise. |
| 51 | static bool RecordMayGoToDestination(const std::string& record_data, |
| 52 | Destination destination) { |
| 53 | // All records sent to destination *_METRIC must be MetricData |
| 54 | // protos due to the way the server is implemented. |
| 55 | if (destination == Destination::EVENT_METRIC || |
| 56 | destination == Destination::TELEMETRY_METRIC || |
| 57 | destination == Destination::INFO_METRIC) { |
| 58 | MetricData metric_data; |
| 59 | const bool is_metric_data = |
| 60 | metric_data.ParseFromString(record_data) && |
| 61 | (metric_data.has_event_data() || metric_data.has_telemetry_data() || |
| 62 | metric_data.has_info_data()); |
| 63 | LOG_IF(ERROR, !is_metric_data) |
| 64 | << "Only MetricData records may be enqueued with destinations: " |
| 65 | "EVENT_METRIC, TELEMETRY_METRIC, or INFO_METRIC"; |
| 66 | return is_metric_data; |
| 67 | } |
| 68 | return true; |
| 69 | } |
| 70 | |
Josh Hilke | fff49137 | 2024-05-09 22:15:55 | [diff] [blame] | 71 | StatusOr<Record> ProduceRecord(std::string dm_token, |
| 72 | Destination destination, |
| 73 | int64_t reserved_space, |
| 74 | std::optional<SourceInfo> source_info, |
| 75 | ReportQueue::RecordProducer record_producer) { |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 76 | // Generate record data. |
Tom Sepez | 709fd293 | 2024-09-11 02:15:22 | [diff] [blame] | 77 | auto record_result = std::move(record_producer).Run(); |
Hong Xu | 5b50cfa | 2023-10-26 21:28:37 | [diff] [blame] | 78 | if (!record_result.has_value()) { |
Josh Hilke | fff49137 | 2024-05-09 22:15:55 | [diff] [blame] | 79 | return base::unexpected(record_result.error()); |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 80 | } |
| 81 | |
Hong Xu | 92482af | 2023-10-25 21:17:55 | [diff] [blame] | 82 | CHECK(RecordMayGoToDestination(record_result.value(), destination)); |
Josh Hilke | 1921032 | 2023-09-19 19:26:35 | [diff] [blame] | 83 | |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 84 | // Augment data. |
| 85 | Record record; |
Hong Xu | 92482af | 2023-10-25 21:17:55 | [diff] [blame] | 86 | *record.mutable_data() = std::move(record_result.value()); |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 87 | record.set_destination(destination); |
Leonid Baraz | b4bdd5d | 2022-11-10 02:08:38 | [diff] [blame] | 88 | if (reserved_space > 0L) { |
| 89 | record.set_reserved_space(reserved_space); |
| 90 | } |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 91 | |
Leonid Baraz | 9975c264 | 2023-02-24 22:09:38 | [diff] [blame] | 92 | // Additional record augmentation for keeping local record copy. |
| 93 | // Note: that must be done before calling `storage->AddRecord` below, |
| 94 | // because later the handler might call it with no need to set this flag. |
| 95 | switch (destination) { |
| 96 | case LOG_UPLOAD: |
| 97 | // It would be better to base the decision on `upload_settings` presence |
| 98 | // in the event, but that would require protobuf reflecion, that is not |
| 99 | // included in Chromium build. So instead we just use `destination`. |
| 100 | record.set_needs_local_unencrypted_copy(true); |
| 101 | break; |
| 102 | default: // Do nothing. |
| 103 | break; |
| 104 | } |
| 105 | |
Josh Hilke | 1921032 | 2023-09-19 19:26:35 | [diff] [blame] | 106 | // |record| with no DM token is assumed to be associated with device DM |
| 107 | // token. |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 108 | if (!dm_token.empty()) { |
| 109 | *record.mutable_dm_token() = std::move(dm_token); |
| 110 | } |
| 111 | |
Vignesh Shenvi | 459a6a62 | 2023-07-12 00:23:16 | [diff] [blame] | 112 | // Augment source info if available. |
| 113 | if (source_info.has_value()) { |
| 114 | *record.mutable_source_info() = std::move(source_info.value()); |
| 115 | } |
| 116 | |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 117 | // Calculate timestamp in microseconds - to match Spanner expectations. |
| 118 | const int64_t time_since_epoch_us = |
Peter Kasting | 08b91b4 | 2023-10-21 03:46:09 | [diff] [blame] | 119 | base::Time::Now().InMillisecondsSinceUnixEpoch() * |
| 120 | base::Time::kMicrosecondsPerMillisecond; |
Hong Xu | e8406051 | 2023-01-31 10:26:18 | [diff] [blame] | 121 | if (time_since_epoch_us > kTime2122) { |
| 122 | // Unusual timestamp. Reject the record even though the record is good |
| 123 | // otherwise, because we can't obtain a reasonable timestamp. We have this |
| 124 | // code block here because server very occasionally detects very large |
Josh Hilke | 1921032 | 2023-09-19 19:26:35 | [diff] [blame] | 125 | // timestamps. The reason could come from occasional irregular system |
| 126 | // time. Filtering out irregular timestamps here should address the |
| 127 | // problem without leaving timestamp-related bugs in the ERP undiscovered |
| 128 | // (should there be any). |
Hong Xu | e8406051 | 2023-01-31 10:26:18 | [diff] [blame] | 129 | base::UmaHistogramBoolean("Browser.ERP.UnusualEnqueueTimestamp", true); |
Josh Hilke | fff49137 | 2024-05-09 22:15:55 | [diff] [blame] | 130 | return base::unexpected(Status( |
Hong Xu | e8406051 | 2023-01-31 10:26:18 | [diff] [blame] | 131 | error::FAILED_PRECONDITION, |
| 132 | base::StrCat( |
| 133 | {"Abnormal system timestamp obtained. Microseconds since epoch: ", |
| 134 | base::NumberToString(time_since_epoch_us)}))); |
Josh Hilke | fff49137 | 2024-05-09 22:15:55 | [diff] [blame] | 135 | } |
Josh Hilke | 67441be | 2024-05-13 18:17:00 | [diff] [blame] | 136 | record.set_timestamp_us(time_since_epoch_us); |
Josh Hilke | fff49137 | 2024-05-09 22:15:55 | [diff] [blame] | 137 | return std::move(record); |
| 138 | } |
| 139 | |
| 140 | // Calls |record_producer|, checks the result and in case of success, forwards |
| 141 | // it to the storage. In production code should be invoked asynchronously, on |
| 142 | // a thread pool (no synchronization expected). |
| 143 | void AddRecordToStorage(scoped_refptr<StorageModuleInterface> storage, |
| 144 | Priority priority, |
| 145 | WrappedRateLimiter::AsyncAcquireCb acquire_cb, |
| 146 | std::string dm_token, |
| 147 | Destination destination, |
| 148 | int64_t reserved_space, |
| 149 | std::optional<SourceInfo> source_info, |
| 150 | ReportQueue::RecordProducer record_producer, |
| 151 | StorageModuleInterface::EnqueueCallback callback) { |
| 152 | auto record_result = |
| 153 | ProduceRecord(dm_token, destination, reserved_space, source_info, |
| 154 | std::move(record_producer)); |
| 155 | |
| 156 | if (!record_result.has_value()) { |
| 157 | std::move(callback).Run(record_result.error()); |
Hong Xu | e8406051 | 2023-01-31 10:26:18 | [diff] [blame] | 158 | return; |
| 159 | } |
Leonid Baraz | 135ad905 | 2023-05-19 15:20:26 | [diff] [blame] | 160 | |
Josh Hilke | fff49137 | 2024-05-09 22:15:55 | [diff] [blame] | 161 | const auto record_size = record_result.value().ByteSizeLong(); |
Leonid Baraz | 135ad905 | 2023-05-19 15:20:26 | [diff] [blame] | 162 | |
| 163 | // Prepare `Storage::AddRecord` as a callback. |
Josh Hilke | fff49137 | 2024-05-09 22:15:55 | [diff] [blame] | 164 | auto add_record_cb = |
| 165 | base::BindOnce(&StorageModuleInterface::AddRecord, storage, priority, |
| 166 | std::move(record_result.value())); |
Leonid Baraz | 135ad905 | 2023-05-19 15:20:26 | [diff] [blame] | 167 | |
| 168 | // Rate-limit event, if required. |
| 169 | if (!acquire_cb) { |
| 170 | // No rate limiter, just add resulting Record to the storage. |
| 171 | std::move(add_record_cb).Run(std::move(callback)); |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 172 | return; |
| 173 | } |
| 174 | |
Leonid Baraz | 135ad905 | 2023-05-19 15:20:26 | [diff] [blame] | 175 | // Add Record only if rate limiter approves. |
| 176 | acquire_cb.Run( |
| 177 | record_size, |
| 178 | base::BindOnce( |
| 179 | [](size_t record_size, |
| 180 | base::OnceCallback<void(StorageModuleInterface::EnqueueCallback |
| 181 | callback)> add_record_cb, |
| 182 | StorageModuleInterface::EnqueueCallback callback, bool acquired) { |
| 183 | if (!acquired) { |
| 184 | std::move(callback).Run( |
| 185 | Status(error::OUT_OF_RANGE, |
| 186 | base::StrCat({"Event size ", |
| 187 | base::NumberToString(record_size), |
| 188 | " rejected by rate limiter"}))); |
| 189 | return; |
| 190 | } |
| 191 | // Add resulting Record to the storage. |
| 192 | std::move(add_record_cb).Run(std::move(callback)); |
| 193 | }, |
| 194 | record_size, std::move(add_record_cb), std::move(callback))); |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 195 | } |
| 196 | } // namespace |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 197 | |
Leonid Baraz | 4d49766a | 2021-10-16 23:50:48 | [diff] [blame] | 198 | void ReportQueueImpl::Create( |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 199 | std::unique_ptr<ReportQueueConfiguration> config, |
Leonid Baraz | 4d49766a | 2021-10-16 23:50:48 | [diff] [blame] | 200 | scoped_refptr<StorageModuleInterface> storage, |
| 201 | base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)> cb) { |
| 202 | std::move(cb).Run(base::WrapUnique<ReportQueueImpl>( |
| 203 | new ReportQueueImpl(std::move(config), storage))); |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 204 | } |
| 205 | |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 206 | ReportQueueImpl::ReportQueueImpl( |
| 207 | std::unique_ptr<ReportQueueConfiguration> config, |
| 208 | scoped_refptr<StorageModuleInterface> storage) |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 209 | : config_(std::move(config)), storage_(storage) { |
| 210 | CHECK(config_); |
| 211 | } |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 212 | |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 213 | ReportQueueImpl::~ReportQueueImpl() = default; |
| 214 | |
| 215 | void ReportQueueImpl::AddProducedRecord(RecordProducer record_producer, |
| 216 | Priority priority, |
| 217 | EnqueueCallback callback) const { |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 218 | const Status status = config_->CheckPolicy(); |
| 219 | if (!status.ok()) { |
| 220 | std::move(callback).Run(status); |
| 221 | return; |
| 222 | } |
| 223 | |
| 224 | if (priority == Priority::UNDEFINED_PRIORITY) { |
| 225 | std::move(callback).Run( |
| 226 | Status(error::INVALID_ARGUMENT, "Priority must be defined")); |
| 227 | return; |
| 228 | } |
| 229 | |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 230 | // Execute |record_producer| on arbitrary thread, analyze the result and send |
| 231 | // it to the Storage, returning with the callback. |
| 232 | base::ThreadPool::PostTask( |
| 233 | FROM_HERE, {base::TaskPriority::BEST_EFFORT}, |
| 234 | base::BindOnce(&AddRecordToStorage, storage_, priority, |
Leonid Baraz | 135ad905 | 2023-05-19 15:20:26 | [diff] [blame] | 235 | config_->is_event_allowed_cb(), config_->dm_token(), |
| 236 | config_->destination(), config_->reserved_space(), |
Vignesh Shenvi | 459a6a62 | 2023-07-12 00:23:16 | [diff] [blame] | 237 | config_->source_info(), std::move(record_producer), |
Irem Uguz | 02a12579 | 2024-01-15 09:28:58 | [diff] [blame] | 238 | // EnqueueCallback must be run on the current thread, we |
| 239 | // need to bind to make sure it's posted to correct thread |
| 240 | // from the ThreadPool. |
| 241 | base::BindPostTaskToCurrentDefault(std::move(callback)))); |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 242 | } |
| 243 | |
Leonid Baraz | 4542951 | 2021-03-12 18:20:12 | [diff] [blame] | 244 | void ReportQueueImpl::Flush(Priority priority, FlushCallback callback) { |
| 245 | storage_->Flush(priority, std::move(callback)); |
| 246 | } |
| 247 | |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 248 | base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)> |
| 249 | ReportQueueImpl::PrepareToAttachActualQueue() const { |
Peter Boström | 77d2135 | 2024-11-13 22:26:11 | [diff] [blame] | 250 | NOTREACHED(); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 251 | } |
| 252 | |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 253 | Destination ReportQueueImpl::GetDestination() const { |
| 254 | return config_->destination(); |
| 255 | } |
| 256 | |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 257 | // Implementation of SpeculativeReportQueueImpl::PendingRecordProducer |
| 258 | |
| 259 | SpeculativeReportQueueImpl::PendingRecordProducer::PendingRecordProducer( |
| 260 | RecordProducer producer, |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 261 | EnqueueCallback callback, |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 262 | Priority priority) |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 263 | : record_producer(std::move(producer)), |
| 264 | record_callback(std::move(callback)), |
| 265 | record_priority(priority) {} |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 266 | |
| 267 | SpeculativeReportQueueImpl::PendingRecordProducer::PendingRecordProducer( |
| 268 | PendingRecordProducer&& other) |
| 269 | : record_producer(std::move(other.record_producer)), |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 270 | record_callback(std::move(other.record_callback)), |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 271 | record_priority(other.record_priority) {} |
| 272 | |
| 273 | SpeculativeReportQueueImpl::PendingRecordProducer::~PendingRecordProducer() = |
| 274 | default; |
| 275 | |
| 276 | SpeculativeReportQueueImpl::PendingRecordProducer& |
| 277 | SpeculativeReportQueueImpl::PendingRecordProducer::operator=( |
| 278 | PendingRecordProducer&& other) { |
| 279 | record_producer = std::move(other.record_producer); |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 280 | record_callback = std::move(other.record_callback); |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 281 | record_priority = other.record_priority; |
| 282 | return *this; |
| 283 | } |
| 284 | |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 285 | // static |
| 286 | std::unique_ptr<SpeculativeReportQueueImpl, base::OnTaskRunnerDeleter> |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 287 | SpeculativeReportQueueImpl::Create( |
| 288 | const SpeculativeConfigSettings& config_settings) { |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 289 | scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner = |
| 290 | base::ThreadPool::CreateSequencedTaskRunner( |
| 291 | {base::TaskPriority::BEST_EFFORT, base::MayBlock()}); |
| 292 | return std::unique_ptr<SpeculativeReportQueueImpl, base::OnTaskRunnerDeleter>( |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 293 | new SpeculativeReportQueueImpl(config_settings, sequenced_task_runner), |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 294 | base::OnTaskRunnerDeleter(sequenced_task_runner)); |
| 295 | } |
| 296 | |
| 297 | SpeculativeReportQueueImpl::SpeculativeReportQueueImpl( |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 298 | const SpeculativeConfigSettings& config_settings, |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 299 | scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner) |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 300 | : sequenced_task_runner_(sequenced_task_runner), |
| 301 | config_settings_(config_settings) { |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 302 | DETACH_FROM_SEQUENCE(sequence_checker_); |
| 303 | } |
| 304 | |
| 305 | SpeculativeReportQueueImpl::~SpeculativeReportQueueImpl() { |
| 306 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
Leonid Baraz | 4a70e5b | 2022-09-30 20:12:04 | [diff] [blame] | 307 | PurgePendingProducers( |
| 308 | Status(error::DATA_LOSS, "The queue is being destructed")); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 309 | } |
| 310 | |
| 311 | void SpeculativeReportQueueImpl::Flush(Priority priority, |
| 312 | FlushCallback callback) { |
| 313 | sequenced_task_runner_->PostTask( |
| 314 | FROM_HERE, |
| 315 | base::BindOnce( |
| 316 | [](Priority priority, FlushCallback callback, |
| 317 | base::WeakPtr<SpeculativeReportQueueImpl> self) { |
| 318 | if (!self) { |
| 319 | std::move(callback).Run( |
| 320 | Status(error::UNAVAILABLE, "Queue has been destructed")); |
Josh Hilke | 1c36a73 | 2024-05-22 22:38:20 | [diff] [blame] | 321 | base::UmaHistogramEnumeration( |
| 322 | reporting::kUmaUnavailableErrorReason, |
| 323 | UnavailableErrorReason::REPORT_QUEUE_DESTRUCTED, |
| 324 | UnavailableErrorReason::MAX_VALUE); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 325 | return; |
| 326 | } |
| 327 | DCHECK_CALLED_ON_VALID_SEQUENCE(self->sequence_checker_); |
Leonid Baraz | 4a70e5b | 2022-09-30 20:12:04 | [diff] [blame] | 328 | if (!self->actual_report_queue_.has_value()) { |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 329 | std::move(callback).Run(Status(error::FAILED_PRECONDITION, |
| 330 | "ReportQueue is not ready yet.")); |
| 331 | return; |
| 332 | } |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 333 | const std::unique_ptr<ReportQueue>& report_queue = |
Leonid Baraz | 4a70e5b | 2022-09-30 20:12:04 | [diff] [blame] | 334 | self->actual_report_queue_.value(); |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 335 | report_queue->Flush(priority, std::move(callback)); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 336 | }, |
| 337 | priority, std::move(callback), weak_ptr_factory_.GetWeakPtr())); |
| 338 | } |
| 339 | |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 340 | void SpeculativeReportQueueImpl::AddProducedRecord( |
| 341 | RecordProducer record_producer, |
| 342 | Priority priority, |
| 343 | EnqueueCallback callback) const { |
| 344 | // Invoke producer on a thread pool, then enqueue record on sequenced task |
| 345 | // runner. |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 346 | sequenced_task_runner_->PostTask( |
| 347 | FROM_HERE, |
| 348 | base::BindOnce(&SpeculativeReportQueueImpl::MaybeEnqueueRecordProducer, |
| 349 | weak_ptr_factory_.GetWeakPtr(), priority, |
Sergey Poromov | 6b9d248 | 2023-05-04 15:47:03 | [diff] [blame] | 350 | base::BindPostTaskToCurrentDefault(std::move(callback)), |
| 351 | std::move(record_producer))); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 352 | } |
| 353 | |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 354 | void SpeculativeReportQueueImpl::MaybeEnqueueRecordProducer( |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 355 | Priority priority, |
Leonid Baraz | f8a9daf | 2022-06-02 01:09:35 | [diff] [blame] | 356 | EnqueueCallback callback, |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 357 | RecordProducer record_producer) const { |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 358 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
Leonid Baraz | 4a70e5b | 2022-09-30 20:12:04 | [diff] [blame] | 359 | if (!actual_report_queue_.has_value()) { |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 360 | // Queue is not ready yet, store the record in the memory queue. |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 361 | pending_record_producers_.emplace(std::move(record_producer), |
| 362 | std::move(callback), priority); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 363 | return; |
| 364 | } |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 365 | // Queue is ready. If memory queue is empty, just forward the record. |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 366 | if (pending_record_producers_.empty()) { |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 367 | const std::unique_ptr<ReportQueue>& report_queue = |
Leonid Baraz | 4a70e5b | 2022-09-30 20:12:04 | [diff] [blame] | 368 | actual_report_queue_.value(); |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 369 | report_queue->AddProducedRecord(std::move(record_producer), priority, |
| 370 | std::move(callback)); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 371 | return; |
| 372 | } |
| 373 | // If memory queue is not empty, attach the new record at the |
| 374 | // end and initiate enqueuing of everything from there. |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 375 | pending_record_producers_.emplace(std::move(record_producer), |
| 376 | std::move(callback), priority); |
| 377 | EnqueuePendingRecordProducers(); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 378 | } |
| 379 | |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 380 | void SpeculativeReportQueueImpl::EnqueuePendingRecordProducers() const { |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 381 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
Leonid Baraz | 48447f14 | 2023-07-31 19:46:21 | [diff] [blame] | 382 | CHECK(actual_report_queue_.has_value()); |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 383 | if (pending_record_producers_.empty()) { |
Ahmed Nasr | d6d02da7 | 2022-04-13 18:07:41 | [diff] [blame] | 384 | return; |
| 385 | } |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 386 | const std::unique_ptr<ReportQueue>& report_queue = |
Leonid Baraz | 4a70e5b | 2022-09-30 20:12:04 | [diff] [blame] | 387 | actual_report_queue_.value(); |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 388 | auto head = std::move(pending_record_producers_.front()); |
| 389 | pending_record_producers_.pop(); |
| 390 | if (pending_record_producers_.empty()) { |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 391 | // Last of the pending records. |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 392 | report_queue->AddProducedRecord(std::move(head.record_producer), |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 393 | head.record_priority, |
| 394 | std::move(head.record_callback)); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 395 | return; |
| 396 | } |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 397 | report_queue->AddProducedRecord( |
Leonid Baraz | 8e2830e | 2022-06-02 20:26:50 | [diff] [blame] | 398 | std::move(head.record_producer), head.record_priority, |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 399 | base::BindPostTask( |
| 400 | sequenced_task_runner_, |
| 401 | base::BindOnce( |
| 402 | [](base::WeakPtr<const SpeculativeReportQueueImpl> self, |
| 403 | EnqueueCallback callback, Status status) { |
| 404 | if (!status.ok()) { |
| 405 | std::move(callback).Run(status); |
| 406 | return; |
| 407 | } |
| 408 | if (!self) { |
| 409 | std::move(callback).Run( |
| 410 | Status(error::UNAVAILABLE, "Queue has been destructed")); |
Josh Hilke | 1c36a73 | 2024-05-22 22:38:20 | [diff] [blame] | 411 | base::UmaHistogramEnumeration( |
| 412 | reporting::kUmaUnavailableErrorReason, |
| 413 | UnavailableErrorReason::REPORT_QUEUE_DESTRUCTED, |
| 414 | UnavailableErrorReason::MAX_VALUE); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 415 | return; |
| 416 | } |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 417 | std::move(callback).Run(status); |
| 418 | self->EnqueuePendingRecordProducers(); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 419 | }, |
Leonid Baraz | 38bc6e22 | 2022-09-29 22:49:29 | [diff] [blame] | 420 | weak_ptr_factory_.GetWeakPtr(), |
| 421 | std::move(head.record_callback)))); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 422 | } |
| 423 | |
| 424 | base::OnceCallback<void(StatusOr<std::unique_ptr<ReportQueue>>)> |
| 425 | SpeculativeReportQueueImpl::PrepareToAttachActualQueue() const { |
| 426 | return base::BindPostTask( |
| 427 | sequenced_task_runner_, |
Leonid Baraz | 08132539 | 2022-11-21 09:13:29 | [diff] [blame] | 428 | base::BindOnce(&SpeculativeReportQueueImpl::AttachActualQueue, |
| 429 | weak_ptr_factory_.GetMutableWeakPtr())); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 430 | } |
| 431 | |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 432 | Destination SpeculativeReportQueueImpl::GetDestination() const { |
| 433 | return config_settings_.destination; |
| 434 | } |
| 435 | |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 436 | void SpeculativeReportQueueImpl::AttachActualQueue( |
Ahmed Nasr | f21e843 | 2022-07-30 00:48:37 | [diff] [blame] | 437 | StatusOr<std::unique_ptr<ReportQueue>> status_or_actual_queue) { |
Leonid Baraz | 08132539 | 2022-11-21 09:13:29 | [diff] [blame] | 438 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 439 | if (actual_report_queue_.has_value()) { |
| 440 | // Already attached, do nothing. |
| 441 | return; |
| 442 | } |
Hong Xu | 5b50cfa | 2023-10-26 21:28:37 | [diff] [blame] | 443 | if (!status_or_actual_queue.has_value()) { |
Leonid Baraz | 08132539 | 2022-11-21 09:13:29 | [diff] [blame] | 444 | // Failed to create actual queue. |
| 445 | // Flush all pending records with this status. |
Hong Xu | 547bcda | 2023-10-27 04:28:01 | [diff] [blame] | 446 | PurgePendingProducers(status_or_actual_queue.error()); |
Leonid Baraz | 08132539 | 2022-11-21 09:13:29 | [diff] [blame] | 447 | return; |
| 448 | } |
| 449 | // Actual report queue succeeded, store it (never to change later). |
Vignesh Shenvi | 17227fa | 2023-11-17 19:07:28 | [diff] [blame] | 450 | CHECK_EQ(config_settings_.destination, |
| 451 | status_or_actual_queue.value()->GetDestination()); |
Hong Xu | 92482af | 2023-10-25 21:17:55 | [diff] [blame] | 452 | actual_report_queue_ = std::move(status_or_actual_queue.value()); |
Leonid Baraz | 08132539 | 2022-11-21 09:13:29 | [diff] [blame] | 453 | EnqueuePendingRecordProducers(); |
Leonid Baraz | b8c27535 | 2021-08-05 00:59:09 | [diff] [blame] | 454 | } |
Leonid Baraz | 4a70e5b | 2022-09-30 20:12:04 | [diff] [blame] | 455 | |
| 456 | void SpeculativeReportQueueImpl::PurgePendingProducers(Status status) const { |
| 457 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| 458 | while (!pending_record_producers_.empty()) { |
| 459 | auto head = std::move(pending_record_producers_.front()); |
| 460 | pending_record_producers_.pop(); |
Josh Hilke | 990e075 | 2024-05-28 20:58:35 | [diff] [blame] | 461 | base::UmaHistogramEnumeration( |
| 462 | reporting::kUmaDataLossErrorReason, |
| 463 | DataLossErrorReason:: |
| 464 | SPECULATIVE_REPORT_QUEUE_DESTRUCTED_BEFORE_RECORDS_ENQUEUED, |
| 465 | DataLossErrorReason::MAX_VALUE); |
Leonid Baraz | 4a70e5b | 2022-09-30 20:12:04 | [diff] [blame] | 466 | std::move(head.record_callback).Run(status); |
| 467 | } |
| 468 | } |
Leonid Baraz | 61437cb | 2021-02-26 20:43:06 | [diff] [blame] | 469 | } // namespace reporting |