chromium/chrome/browser/metrics/structured/arena_event_buffer.h

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

#ifndef CHROME_BROWSER_METRICS_STRUCTURED_ARENA_EVENT_BUFFER_H_
#define CHROME_BROWSER_METRICS_STRUCTURED_ARENA_EVENT_BUFFER_H_

#include <cstdint>
#include <memory>

#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/timer/timer.h"
#include "chrome/browser/metrics/structured/profile_observer.h"
#include "components/metrics/structured/lib/arena_persistent_proto.h"
#include "components/metrics/structured/lib/event_buffer.h"
#include "components/metrics/structured/proto/event_storage.pb.h"
#include "third_party/metrics_proto/structured_data.pb.h"

namespace base {
class FilePath;
}

class Profile;

namespace metrics::structured {

// An implementation of an EventBuffer that stored events in an
// ArenaPersistentProto.
//
// Since getting the in-memory size of the proto is not available in Chromium,
// an estimation is used. Events are serialized by copying the events into a
// RepeatedPtrField. This is necessary because the events are stored in an arena
// and the returned RepeatedPtrField isn't allocated from the same arena.
// Events are flushed by serializing the proto and writing it into the path
// provided. An estimation is used to determine the size of an event instead of
// getting the actual size. This buffer is flushed when the designated resources
// have been consumed.
// TODO(b/347752634) Refactor ProfileObserver classes to use a single helper
// class.
class ArenaEventBuffer : public EventBuffer<StructuredEventProto>,
                         public ProfileObserver {
 public:
  ArenaEventBuffer(const base::FilePath& path,
                   base::TimeDelta write_delay,
                   uint64_t max_size_bytes);

  ~ArenaEventBuffer() override;

  // EventBuffer:
  Result AddEvent(StructuredEventProto event) override;
  void Purge() override;
  uint64_t Size() override;
  google::protobuf::RepeatedPtrField<StructuredEventProto> Serialize() override;
  void Flush(const base::FilePath& path, FlushedCallback callback) override;

  // ProfileObserver:
  void ProfileAdded(const Profile& profile) override;

  // Updates the path of the persistent proto and merges the content of |path|
  // into |events_|.
  void UpdatePath(const base::FilePath& path);

  const google::protobuf::Arena* arena() const { return events_->arena(); }

  ArenaPersistentProto<EventsProto>& proto() { return *events_; }
  const ArenaPersistentProto<EventsProto>& proto() const { return *events_; }

  // Computes an estimate size in bytes of an event.
  //
  // The estimation is computed by summing:
  // * Size of StructuredEventProto
  // * Size of Metrics, times the number of metrics
  // * Size of event sequence metadata if it has one.
  static uint64_t EstimateEventSize(const StructuredEventProto& event);

 private:
  void OnEventRead(const ReadStatus status);

  void OnEventWrite(const WriteStatus status);

  // Called periodically to backup |events_| to disk.
  void BackupTask();

  // The proto to store the events.
  std::unique_ptr<ArenaPersistentProto<EventsProto>> events_;

  // A timer to periodically backup |event_| to disk.
  base::RepeatingTimer backup_timer_;

  // A cache of events received before the buffer was ready store them.
  std::vector<StructuredEventProto> pre_init_events_;

  scoped_refptr<base::SequencedTaskRunner> task_runner_;
  base::WeakPtrFactory<ArenaEventBuffer> weak_factory_{this};
};
}  // namespace metrics::structured

#endif  // CHROME_BROWSER_METRICS_STRUCTURED_ARENA_EVENT_BUFFER_H_