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
# 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
  @profiler_failed = 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

#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)


80
81
82
# File 'lib/datadog/profiling/scheduler.rb', line 80

def loop_wait_before_first_iteration?
  true
end

#mark_profiler_failedObject

This is called by the Profiler class whenever an issue happened in the profiler. This makes sure that even if there is data to be flushed, we don’t try to flush it.



86
87
88
# File 'lib/datadog/profiling/scheduler.rb', line 86

def mark_profiler_failed
  @profiler_failed = true
end

#perform(on_failure_proc) ⇒ Object



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

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
  raise
ensure
  Datadog.logger.debug("#flush was interrupted or failed before it could complete") if interrupted
end

#reset_after_forkObject



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

def reset_after_fork
  exporter.reset_after_fork
end

#start(on_failure_proc: nil) ⇒ Object



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

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

#work_pending?Boolean

Returns:

  • (Boolean)


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

def work_pending?
  !profiler_failed && exporter.can_flush?
end