Class: Datadog::Profiling::Scheduler

Inherits:
Core::Worker show all
Includes:
Core::Workers::Polling
Defined in:
lib/datadog/profiling/scheduler.rb

Overview

Periodically (every interval, 60 seconds by default) takes a profile from the Exporter and reports it using the configured transport. Runs on its own background thread.

Constant Summary collapse

MINIMUM_INTERVAL_SECONDS =
0
DEFAULT_FLUSH_JITTER_MAXIMUM_SECONDS =

We sleep for at most this duration seconds before reporting data to avoid multi-process applications all reporting profiles at the exact same time

3

Constants included from Core::Workers::Polling

Core::Workers::Polling::DEFAULT_SHUTDOWN_TIMEOUT

Instance Attribute Summary

Attributes inherited from Core::Worker

#task

Instance Method Summary collapse

Methods included from Core::Workers::Polling

#enabled=, #enabled?, included, #stop

Constructor Details

#initialize(exporter:, transport:, interval:, fork_policy: Core::Workers::Async::Thread::FORK_POLICY_RESTART, enabled: true) ⇒ Scheduler

Returns a new instance of Scheduler.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/datadog/profiling/scheduler.rb', line 31

def initialize(
  exporter:,
  transport:,
  interval:, fork_policy: Core::Workers::Async::Thread::FORK_POLICY_RESTART, # Restart in forks by default, # seconds
  enabled: true
)
  @exporter = exporter
  @transport = transport
  @reporting_disabled = false
  @stop_requested = false

  # Workers::Async::Thread settings
  self.fork_policy = fork_policy

  # Workers::IntervalLoop settings
  self.loop_base_interval = interval

  # Workers::Polling settings
  self.enabled = enabled
end

Instance Method Details

#disable_reportingObject

Called by the Profiler when it wants to prevent any further reporting, even if there is data to be flushed. Used when the profiler failed or we’re otherwise in a hurry to shut down.



89
90
91
# File 'lib/datadog/profiling/scheduler.rb', line 89

def disable_reporting
  @reporting_disabled = true
end

#loop_wait_before_first_iteration?Boolean

Configure Workers::IntervalLoop to not report immediately when scheduler starts

When a scheduler gets created (or reset), we don’t want it to immediately try to flush; we want it to wait for the loop wait time first. This avoids an issue where the scheduler reported a mostly-empty profile if the application just started but this thread took a bit longer so there’s already profiling data in the exporter.

Returns:

  • (Boolean)


82
83
84
# File 'lib/datadog/profiling/scheduler.rb', line 82

def loop_wait_before_first_iteration?
  true
end

#perform(on_failure_proc) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/datadog/profiling/scheduler.rb', line 56

def perform(on_failure_proc)
  # A profiling flush may be called while the VM is shutting down, to report the last profile. When we do so,
  # we impose a strict timeout. This means this last profile may or may not be sent, depending on if the flush can
  # successfully finish in the strict timeout.
  # This can be somewhat confusing (why did it not get reported?), so let's at least log what happened.
  interrupted = true

  flush_and_wait
  interrupted = false
rescue Exception => e # rubocop:disable Lint/RescueException
  Datadog.logger.warn(
    "Profiling::Scheduler thread error. " \
    "Cause: #{e.class.name} #{e.message} Location: #{Array(e.backtrace).first}"
  )
  on_failure_proc&.call
  Datadog::Core::Telemetry::Logger.report(e, description: "Profiling::Scheduler thread error")
  raise
ensure
  Datadog.logger.debug("#flush was interrupted or failed before it could complete") if interrupted
end

#reset_after_forkObject



97
98
99
# File 'lib/datadog/profiling/scheduler.rb', line 97

def reset_after_fork
  exporter.reset_after_fork
end

#start(on_failure_proc: nil) ⇒ Object



52
53
54
# File 'lib/datadog/profiling/scheduler.rb', line 52

def start(on_failure_proc: nil)
  perform(on_failure_proc)
end

#work_pending?Boolean

Returns:

  • (Boolean)


93
94
95
# File 'lib/datadog/profiling/scheduler.rb', line 93

def work_pending?
  !reporting_disabled && exporter.can_flush? && (run_loop? || !stop_requested?)
end