chromium/chromecast/cast_core/runtime/browser/cast_runtime_metrics_recorder_service.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromecast/cast_core/runtime/browser/cast_runtime_metrics_recorder_service.h"

#include "base/logging.h"
#include "chromecast/cast_core/runtime/browser/cast_runtime_action_recorder.h"
#include "chromecast/cast_core/runtime/browser/cast_runtime_histogram_flattener.h"
#include "chromecast/cast_core/runtime/browser/cast_runtime_metrics_recorder.h"
#include "third_party/cast_core/public/src/proto/metrics/metrics_recorder.pb.h"

namespace chromecast {
namespace {

constexpr size_t kMaxBatchSize = 50;

}  // namespace

CastRuntimeMetricsRecorderService::CastRuntimeMetricsRecorderService(
    CastRuntimeMetricsRecorder* metrics_recorder,
    CastRuntimeActionRecorder* action_recorder,
    RecordMetricsCallback record_metrics_callback,
    base::TimeDelta report_interval)
    : metrics_recorder_(metrics_recorder),
      action_recorder_(action_recorder),
      record_metrics_callback_(record_metrics_callback) {
  report_timer_.Start(
      FROM_HERE, report_interval,
      base::BindRepeating(&CastRuntimeMetricsRecorderService::Report,
                          weak_factory_.GetWeakPtr()));
}

CastRuntimeMetricsRecorderService::~CastRuntimeMetricsRecorderService() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}

void CastRuntimeMetricsRecorderService::OnCloseSoon(
    base::OnceClosure complete_callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (flush_complete_callback_) {
    return;
  }
  report_timer_.Stop();
  flush_complete_callback_ = std::move(complete_callback);
  Report();
}

void CastRuntimeMetricsRecorderService::Report() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  std::vector<cast::metrics::Event> events = metrics_recorder_->TakeEvents();
  for (auto& histogram : GetHistogramDeltas()) {
    cast::metrics::Event event;
    *event.mutable_histogram() = std::move(histogram);
    events.push_back(std::move(event));
  }

  for (auto& action : action_recorder_->TakeEvents()) {
    cast::metrics::Event event;
    *event.mutable_user_action_event() = std::move(action);
    events.push_back(std::move(event));
  }

  std::move(events.begin(), events.end(), std::back_inserter(send_buffer_));

  DrainBuffer();
}

void CastRuntimeMetricsRecorderService::OnMetricsRecorded() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(ack_pending_);
  ack_pending_ = false;
  DrainBuffer();
}

void CastRuntimeMetricsRecorderService::DrainBuffer() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (ack_pending_) {
    return;
  }
  if (send_buffer_.empty()) {
    if (flush_complete_callback_) {
      std::move(flush_complete_callback_).Run();
    }
    return;
  }
  // NOTE: Ordering doesn't matter, so we can more efficiently tear off the end
  // of the vector each time.
  size_t start_index =
      send_buffer_.size() - std::min(send_buffer_.size(), kMaxBatchSize);
  cast::metrics::RecordRequest request;
  *request.mutable_event() = {send_buffer_.begin() + start_index,
                              send_buffer_.end()};
  send_buffer_.resize(start_index);

  DVLOG(2) << "Sending metrics";
  ack_pending_ = true;
  record_metrics_callback_.Run(
      std::move(request),
      base::BindOnce(&CastRuntimeMetricsRecorderService::OnMetricsRecorded,
                     weak_factory_.GetWeakPtr()));
}

}  // namespace chromecast