datadog 2.22.0 → 2.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +100 -1
  3. data/ext/LIBDATADOG_DEVELOPMENT.md +1 -58
  4. data/ext/datadog_profiling_native_extension/collectors_stack.c +21 -5
  5. data/ext/datadog_profiling_native_extension/crashtracking_runtime_stacks.c +239 -0
  6. data/ext/datadog_profiling_native_extension/datadog_ruby_common.h +1 -1
  7. data/ext/datadog_profiling_native_extension/extconf.rb +9 -4
  8. data/ext/datadog_profiling_native_extension/heap_recorder.c +1 -1
  9. data/ext/datadog_profiling_native_extension/private_vm_api_access.c +12 -0
  10. data/ext/datadog_profiling_native_extension/private_vm_api_access.h +4 -0
  11. data/ext/datadog_profiling_native_extension/profiling.c +2 -0
  12. data/ext/libdatadog_api/datadog_ruby_common.h +1 -1
  13. data/ext/libdatadog_api/feature_flags.c +554 -0
  14. data/ext/libdatadog_api/feature_flags.h +5 -0
  15. data/ext/libdatadog_api/init.c +2 -0
  16. data/ext/libdatadog_api/library_config.c +12 -11
  17. data/ext/libdatadog_extconf_helpers.rb +1 -1
  18. data/lib/datadog/appsec/api_security/route_extractor.rb +23 -6
  19. data/lib/datadog/appsec/api_security/sampler.rb +7 -4
  20. data/lib/datadog/appsec/assets/blocked.html +8 -0
  21. data/lib/datadog/appsec/assets/blocked.json +1 -1
  22. data/lib/datadog/appsec/assets/blocked.text +3 -1
  23. data/lib/datadog/appsec/assets.rb +1 -1
  24. data/lib/datadog/appsec/context.rb +2 -1
  25. data/lib/datadog/appsec/remote.rb +5 -9
  26. data/lib/datadog/appsec/response.rb +18 -4
  27. data/lib/datadog/appsec/security_engine/result.rb +2 -1
  28. data/lib/datadog/core/configuration/components.rb +30 -3
  29. data/lib/datadog/core/configuration/config_helper.rb +2 -2
  30. data/lib/datadog/core/configuration/deprecations.rb +2 -2
  31. data/lib/datadog/core/configuration/option_definition.rb +4 -2
  32. data/lib/datadog/core/configuration/options.rb +8 -5
  33. data/lib/datadog/core/configuration/settings.rb +28 -3
  34. data/lib/datadog/core/configuration/supported_configurations.rb +332 -302
  35. data/lib/datadog/core/ddsketch.rb +0 -2
  36. data/lib/datadog/core/environment/cgroup.rb +52 -25
  37. data/lib/datadog/core/environment/container.rb +140 -46
  38. data/lib/datadog/core/environment/ext.rb +7 -0
  39. data/lib/datadog/core/environment/process.rb +87 -0
  40. data/lib/datadog/core/feature_flags.rb +61 -0
  41. data/lib/datadog/core/rate_limiter.rb +9 -1
  42. data/lib/datadog/core/remote/client/capabilities.rb +7 -0
  43. data/lib/datadog/core/remote/client.rb +14 -6
  44. data/lib/datadog/core/remote/component.rb +6 -4
  45. data/lib/datadog/core/remote/configuration/content.rb +15 -2
  46. data/lib/datadog/core/remote/configuration/digest.rb +14 -7
  47. data/lib/datadog/core/remote/configuration/repository.rb +1 -1
  48. data/lib/datadog/core/remote/configuration/target.rb +13 -6
  49. data/lib/datadog/core/remote/transport/config.rb +4 -25
  50. data/lib/datadog/core/remote/transport/http/config.rb +10 -50
  51. data/lib/datadog/core/remote/transport/http/negotiation.rb +14 -44
  52. data/lib/datadog/core/remote/transport/http.rb +15 -24
  53. data/lib/datadog/core/remote/transport/negotiation.rb +8 -33
  54. data/lib/datadog/core/remote/worker.rb +25 -37
  55. data/lib/datadog/core/tag_builder.rb +0 -4
  56. data/lib/datadog/core/tag_normalizer.rb +84 -0
  57. data/lib/datadog/core/telemetry/component.rb +59 -16
  58. data/lib/datadog/core/telemetry/event/app_started.rb +86 -49
  59. data/lib/datadog/core/telemetry/event/synth_app_client_configuration_change.rb +27 -4
  60. data/lib/datadog/core/telemetry/logger.rb +2 -2
  61. data/lib/datadog/core/telemetry/logging.rb +2 -8
  62. data/lib/datadog/core/telemetry/metrics_manager.rb +9 -0
  63. data/lib/datadog/core/telemetry/request.rb +17 -3
  64. data/lib/datadog/core/telemetry/transport/http/telemetry.rb +3 -34
  65. data/lib/datadog/core/telemetry/transport/http.rb +21 -16
  66. data/lib/datadog/core/telemetry/transport/telemetry.rb +3 -11
  67. data/lib/datadog/core/telemetry/worker.rb +88 -32
  68. data/lib/datadog/core/transport/ext.rb +2 -0
  69. data/lib/datadog/core/transport/http/api/endpoint.rb +9 -4
  70. data/lib/datadog/core/transport/http/api/instance.rb +4 -21
  71. data/lib/datadog/core/transport/http/builder.rb +9 -5
  72. data/lib/datadog/core/transport/http/client.rb +80 -0
  73. data/lib/datadog/core/transport/http.rb +22 -19
  74. data/lib/datadog/core/transport/response.rb +9 -0
  75. data/lib/datadog/core/transport/transport.rb +90 -0
  76. data/lib/datadog/core/utils/array.rb +29 -0
  77. data/lib/datadog/{appsec/api_security → core/utils}/lru_cache.rb +10 -21
  78. data/lib/datadog/core/utils/network.rb +3 -1
  79. data/lib/datadog/core/utils/only_once_successful.rb +8 -2
  80. data/lib/datadog/core/utils/time.rb +1 -1
  81. data/lib/datadog/core/utils.rb +2 -0
  82. data/lib/datadog/core/workers/async.rb +10 -1
  83. data/lib/datadog/core/workers/interval_loop.rb +44 -3
  84. data/lib/datadog/core/workers/polling.rb +2 -0
  85. data/lib/datadog/core/workers/queue.rb +100 -1
  86. data/lib/datadog/data_streams/configuration/settings.rb +49 -0
  87. data/lib/datadog/data_streams/configuration.rb +11 -0
  88. data/lib/datadog/data_streams/ext.rb +11 -0
  89. data/lib/datadog/data_streams/extensions.rb +16 -0
  90. data/lib/datadog/data_streams/pathway_context.rb +169 -0
  91. data/lib/datadog/data_streams/processor.rb +509 -0
  92. data/lib/datadog/data_streams/transport/http/stats.rb +52 -0
  93. data/lib/datadog/data_streams/transport/http.rb +40 -0
  94. data/lib/datadog/data_streams/transport/stats.rb +46 -0
  95. data/lib/datadog/data_streams.rb +100 -0
  96. data/lib/datadog/di/component.rb +0 -16
  97. data/lib/datadog/di/contrib/active_record.rb +31 -5
  98. data/lib/datadog/di/el/compiler.rb +8 -4
  99. data/lib/datadog/di/el/evaluator.rb +1 -1
  100. data/lib/datadog/di/error.rb +9 -0
  101. data/lib/datadog/di/instrumenter.rb +93 -34
  102. data/lib/datadog/di/probe.rb +20 -0
  103. data/lib/datadog/di/probe_builder.rb +2 -1
  104. data/lib/datadog/di/probe_manager.rb +47 -33
  105. data/lib/datadog/di/probe_notification_builder.rb +77 -25
  106. data/lib/datadog/di/proc_responder.rb +32 -0
  107. data/lib/datadog/di/remote.rb +89 -84
  108. data/lib/datadog/di/transport/diagnostics.rb +8 -36
  109. data/lib/datadog/di/transport/http/diagnostics.rb +1 -33
  110. data/lib/datadog/di/transport/http/input.rb +1 -33
  111. data/lib/datadog/di/transport/http.rb +32 -17
  112. data/lib/datadog/di/transport/input.rb +67 -34
  113. data/lib/datadog/di.rb +61 -5
  114. data/lib/datadog/open_feature/component.rb +60 -0
  115. data/lib/datadog/open_feature/configuration.rb +27 -0
  116. data/lib/datadog/open_feature/evaluation_engine.rb +70 -0
  117. data/lib/datadog/open_feature/exposures/batch_builder.rb +32 -0
  118. data/lib/datadog/open_feature/exposures/buffer.rb +43 -0
  119. data/lib/datadog/open_feature/exposures/deduplicator.rb +30 -0
  120. data/lib/datadog/open_feature/exposures/event.rb +60 -0
  121. data/lib/datadog/open_feature/exposures/reporter.rb +40 -0
  122. data/lib/datadog/open_feature/exposures/worker.rb +116 -0
  123. data/lib/datadog/open_feature/ext.rb +14 -0
  124. data/lib/datadog/open_feature/native_evaluator.rb +38 -0
  125. data/lib/datadog/open_feature/noop_evaluator.rb +26 -0
  126. data/lib/datadog/open_feature/provider.rb +141 -0
  127. data/lib/datadog/open_feature/remote.rb +67 -0
  128. data/lib/datadog/open_feature/resolution_details.rb +35 -0
  129. data/lib/datadog/open_feature/transport.rb +70 -0
  130. data/lib/datadog/open_feature.rb +19 -0
  131. data/lib/datadog/opentelemetry/api/baggage.rb +1 -1
  132. data/lib/datadog/opentelemetry/configuration/settings.rb +159 -0
  133. data/lib/datadog/opentelemetry/metrics.rb +117 -0
  134. data/lib/datadog/opentelemetry/sdk/configurator.rb +25 -1
  135. data/lib/datadog/opentelemetry/sdk/metrics_exporter.rb +35 -0
  136. data/lib/datadog/opentelemetry.rb +3 -0
  137. data/lib/datadog/profiling/collectors/code_provenance.rb +41 -7
  138. data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +1 -1
  139. data/lib/datadog/profiling/collectors/idle_sampling_helper.rb +1 -1
  140. data/lib/datadog/profiling/collectors/info.rb +2 -1
  141. data/lib/datadog/profiling/component.rb +12 -11
  142. data/lib/datadog/profiling/http_transport.rb +4 -1
  143. data/lib/datadog/profiling/profiler.rb +4 -0
  144. data/lib/datadog/profiling/tag_builder.rb +36 -3
  145. data/lib/datadog/profiling.rb +1 -2
  146. data/lib/datadog/single_step_instrument.rb +1 -1
  147. data/lib/datadog/tracing/configuration/ext.rb +9 -0
  148. data/lib/datadog/tracing/configuration/settings.rb +74 -0
  149. data/lib/datadog/tracing/contrib/action_pack/action_controller/instrumentation.rb +4 -4
  150. data/lib/datadog/tracing/contrib/action_pack/utils.rb +1 -2
  151. data/lib/datadog/tracing/contrib/active_job/log_injection.rb +21 -7
  152. data/lib/datadog/tracing/contrib/active_job/patcher.rb +5 -1
  153. data/lib/datadog/tracing/contrib/aws/instrumentation.rb +4 -2
  154. data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +4 -1
  155. data/lib/datadog/tracing/contrib/excon/configuration/settings.rb +11 -3
  156. data/lib/datadog/tracing/contrib/extensions.rb +10 -2
  157. data/lib/datadog/tracing/contrib/faraday/configuration/settings.rb +11 -7
  158. data/lib/datadog/tracing/contrib/grape/configuration/settings.rb +7 -3
  159. data/lib/datadog/tracing/contrib/graphql/unified_trace.rb +22 -17
  160. data/lib/datadog/tracing/contrib/http/configuration/settings.rb +11 -3
  161. data/lib/datadog/tracing/contrib/httpclient/configuration/settings.rb +11 -3
  162. data/lib/datadog/tracing/contrib/httprb/configuration/settings.rb +11 -3
  163. data/lib/datadog/tracing/contrib/kafka/instrumentation/consumer.rb +66 -0
  164. data/lib/datadog/tracing/contrib/kafka/instrumentation/producer.rb +66 -0
  165. data/lib/datadog/tracing/contrib/kafka/patcher.rb +14 -0
  166. data/lib/datadog/tracing/contrib/karafka/framework.rb +30 -0
  167. data/lib/datadog/tracing/contrib/karafka/monitor.rb +11 -0
  168. data/lib/datadog/tracing/contrib/karafka/patcher.rb +35 -4
  169. data/lib/datadog/tracing/contrib/rack/middlewares.rb +59 -27
  170. data/lib/datadog/tracing/contrib/rack/route_inference.rb +53 -0
  171. data/lib/datadog/tracing/contrib/rails/middlewares.rb +2 -2
  172. data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +4 -1
  173. data/lib/datadog/tracing/contrib/roda/instrumentation.rb +3 -1
  174. data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +3 -1
  175. data/lib/datadog/tracing/contrib/status_range_matcher.rb +9 -1
  176. data/lib/datadog/tracing/contrib/utils/quantization/hash.rb +3 -1
  177. data/lib/datadog/tracing/contrib/waterdrop/configuration/settings.rb +27 -0
  178. data/lib/datadog/tracing/contrib/waterdrop/distributed/propagation.rb +48 -0
  179. data/lib/datadog/tracing/contrib/waterdrop/ext.rb +17 -0
  180. data/lib/datadog/tracing/contrib/waterdrop/integration.rb +43 -0
  181. data/lib/datadog/tracing/contrib/waterdrop/middleware.rb +46 -0
  182. data/lib/datadog/tracing/contrib/waterdrop/patcher.rb +49 -0
  183. data/lib/datadog/tracing/contrib/waterdrop/producer.rb +50 -0
  184. data/lib/datadog/tracing/contrib/waterdrop.rb +37 -0
  185. data/lib/datadog/tracing/contrib.rb +1 -0
  186. data/lib/datadog/tracing/diagnostics/environment_logger.rb +1 -1
  187. data/lib/datadog/tracing/metadata/ext.rb +1 -1
  188. data/lib/datadog/tracing/remote.rb +1 -9
  189. data/lib/datadog/tracing/span_event.rb +2 -2
  190. data/lib/datadog/tracing/span_operation.rb +9 -4
  191. data/lib/datadog/tracing/trace_operation.rb +44 -6
  192. data/lib/datadog/tracing/tracer.rb +42 -16
  193. data/lib/datadog/tracing/transport/http/client.rb +12 -26
  194. data/lib/datadog/tracing/transport/http/traces.rb +2 -50
  195. data/lib/datadog/tracing/transport/http.rb +15 -9
  196. data/lib/datadog/tracing/transport/io/client.rb +1 -1
  197. data/lib/datadog/tracing/transport/trace_formatter.rb +11 -0
  198. data/lib/datadog/tracing/transport/traces.rb +9 -71
  199. data/lib/datadog/tracing/workers/trace_writer.rb +5 -0
  200. data/lib/datadog/tracing/writer.rb +1 -0
  201. data/lib/datadog/version.rb +2 -2
  202. data/lib/datadog.rb +2 -0
  203. metadata +78 -21
  204. data/lib/datadog/core/remote/transport/http/api.rb +0 -53
  205. data/lib/datadog/core/remote/transport/http/client.rb +0 -49
  206. data/lib/datadog/core/telemetry/transport/http/api.rb +0 -43
  207. data/lib/datadog/core/telemetry/transport/http/client.rb +0 -49
  208. data/lib/datadog/core/transport/http/api/spec.rb +0 -36
  209. data/lib/datadog/di/transport/http/api.rb +0 -42
  210. data/lib/datadog/di/transport/http/client.rb +0 -47
  211. data/lib/datadog/opentelemetry/api/baggage.rbs +0 -26
  212. data/lib/datadog/tracing/transport/http/api.rb +0 -44
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'http/client'
4
+
5
+ module Datadog
6
+ module Core
7
+ module Transport
8
+ # Raised when configured with an unknown API version
9
+ class UnknownApiVersionError < StandardError
10
+ attr_reader :version
11
+
12
+ def initialize(version)
13
+ super
14
+
15
+ @version = version
16
+ end
17
+
18
+ def message
19
+ "No matching transport API for version #{version}!"
20
+ end
21
+ end
22
+
23
+ # Raised when the API verson to downgrade to does not map to a
24
+ # defined API.
25
+ class NoDowngradeAvailableError < StandardError
26
+ attr_reader :version
27
+
28
+ def initialize(version)
29
+ super
30
+
31
+ @version = version
32
+ end
33
+
34
+ def message
35
+ "No downgrade from transport API version #{version} is available!"
36
+ end
37
+ end
38
+
39
+ # Base class for transports.
40
+ class Transport
41
+ attr_reader :client, :apis, :default_api, :current_api_id, :logger
42
+
43
+ class << self
44
+ # The HTTP client class to use for requests, derived from
45
+ # Core::Transport::HTTP::Client.
46
+ #
47
+ # Important: this attribute is NOT inherited by derived classes -
48
+ # it must be set by every Transport class that wants to have a
49
+ # non-default HTTP::Client instance.
50
+ attr_accessor :http_client_class
51
+ end
52
+
53
+ def initialize(apis, default_api, logger:)
54
+ @apis = apis
55
+ @default_api = default_api
56
+ @logger = logger
57
+
58
+ set_api!(default_api)
59
+ end
60
+
61
+ def current_api
62
+ apis[current_api_id]
63
+ end
64
+
65
+ private
66
+
67
+ def set_api!(api_id)
68
+ raise UnknownApiVersionError, api_id unless apis.key?(api_id)
69
+
70
+ @current_api_id = api_id
71
+ client_class = self.class.http_client_class || Core::Transport::HTTP::Client
72
+ @client = client_class.new(current_api, logger: logger) # steep:ignore
73
+ end
74
+
75
+ def downgrade?(response)
76
+ return false unless apis.fallbacks.key?(current_api_id)
77
+
78
+ response.not_found? || response.unsupported?
79
+ end
80
+
81
+ def downgrade!
82
+ downgrade_api_id = apis.fallbacks[current_api_id]
83
+ raise NoDowngradeAvailableError, current_api_id if downgrade_api_id.nil?
84
+
85
+ set_api!(downgrade_api_id)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module Core
5
+ module Utils
6
+ # Common array-related utility functions.
7
+ module Array
8
+ def self.filter_map(array, &block)
9
+ if array.respond_to?(:filter_map)
10
+ # DEV Supported since Ruby 2.7, saves an intermediate object creation
11
+ array.filter_map(&block)
12
+ elsif array.is_a?(Enumerator::Lazy)
13
+ # You would think that .compact would work here, but it does not:
14
+ # the result of .map could be an Enumerator::Lazy instance which
15
+ # does not implement #compact on Ruby 2.5/2.6.
16
+ array.map(&block).reject do |item|
17
+ item.nil?
18
+ end
19
+ else
20
+ array.each_with_object([]) do |item, memo|
21
+ new_item = block.call(item)
22
+ memo.push(new_item) unless new_item.nil?
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -3,13 +3,13 @@
3
3
  require 'forwardable'
4
4
 
5
5
  module Datadog
6
- module AppSec
7
- module APISecurity
6
+ module Core
7
+ module Utils
8
8
  # An LRU (Least Recently Used) cache implementation that relies on the
9
9
  # Ruby 1.9+ `Hash` implementation that guarantees insertion order.
10
10
  #
11
11
  # WARNING: This implementation is NOT thread-safe and should be used
12
- # in a single-threaded context.
12
+ # in a single-threaded context or guarded by Mutex.
13
13
  class LRUCache
14
14
  extend Forwardable
15
15
 
@@ -30,25 +30,14 @@ module Datadog
30
30
  end
31
31
  end
32
32
 
33
- def store(key, value)
34
- return @store[key] = value if @store.delete(key)
35
-
36
- # NOTE: evict the oldest entry if store reached the maximum allowed size
37
- @store.shift if @store.size >= @max_size
38
- @store[key] = value
39
- end
40
-
41
- # NOTE: If the key exists, it's moved to the end of the list and
42
- # if does not, the given block will be executed and the result
43
- # will be stored (which will add it to the end of the list).
44
- def fetch_or_store(key)
45
- if (entry = @store.delete(key))
46
- return @store[key] = entry
33
+ def []=(key, value)
34
+ if @store.delete(key)
35
+ @store[key] = value
36
+ else
37
+ # NOTE: evict the oldest entry if store reached the maximum allowed size
38
+ @store.shift if @store.size >= @max_size
39
+ @store[key] = value
47
40
  end
48
-
49
- # NOTE: evict the oldest entry if store reached the maximum allowed size
50
- @store.shift if @store.size >= @max_size
51
- @store[key] = yield
52
41
  end
53
42
  end
54
43
  end
@@ -21,6 +21,8 @@ module Datadog
21
21
  cf-connecting-ipv6
22
22
  ].freeze
23
23
 
24
+ CGNAT_IP_RANGE = IPAddr.new('100.64.0.0/10')
25
+
24
26
  class << self
25
27
  # Returns a client IP associated with the request if it was
26
28
  # retrieved successfully.
@@ -131,7 +133,7 @@ module Datadog
131
133
  end
132
134
 
133
135
  def global_ip?(parsed_ip)
134
- parsed_ip && !parsed_ip.private? && !parsed_ip.loopback? && !parsed_ip.link_local?
136
+ parsed_ip && !parsed_ip.private? && !parsed_ip.loopback? && !parsed_ip.link_local? && !CGNAT_IP_RANGE.include?(parsed_ip)
135
137
  end
136
138
  end
137
139
  end
@@ -29,7 +29,11 @@ module Datadog
29
29
  # In https://github.com/DataDog/dd-trace-rb/pull/1398#issuecomment-797378810 we have a discussion of alternatives,
30
30
  # including an alternative implementation that is Ractor-safe once spent.
31
31
  class OnlyOnceSuccessful < OnlyOnce
32
- def initialize(limit = 0)
32
+ def initialize(limit = nil)
33
+ if limit && limit <= 0
34
+ raise ArgumentError, "Limit must be a positive integer if provided: #{limit}"
35
+ end
36
+
33
37
  super()
34
38
 
35
39
  @limit = limit
@@ -63,7 +67,9 @@ module Datadog
63
67
 
64
68
  private
65
69
 
70
+ # Use this method only after checking that limit is not nil.
66
71
  def check_limit!
72
+ # @type ivar @limit: Integer
67
73
  if @retries >= @limit
68
74
  @failed = true
69
75
  @ran_once = true
@@ -71,7 +77,7 @@ module Datadog
71
77
  end
72
78
 
73
79
  def limited?
74
- !@limit.nil? && @limit.positive?
80
+ !@limit.nil?
75
81
  end
76
82
 
77
83
  def reset_ran_once_state_for_tests
@@ -10,7 +10,7 @@ module Datadog
10
10
  # Current monotonic time
11
11
  #
12
12
  # @param unit [Symbol] unit for the resulting value, same as ::Process#clock_gettime, defaults to :float_second
13
- # @return [Numeric] timestamp in the requested unit, since some unspecified starting point
13
+ # @return [Float|Integer] timestamp in the requested unit, since some unspecified starting point
14
14
  def get_time(unit = :float_second)
15
15
  Process.clock_gettime(Process::CLOCK_MONOTONIC, unit)
16
16
  end
@@ -38,6 +38,8 @@ module Datadog
38
38
 
39
39
  # Ensure `str` is a valid UTF-8, ready to be
40
40
  # sent through the tracer transport.
41
+ # DEV-3.0: This method should unconditionally handle invalid byte sequences
42
+ # DEV-3.0: and return a safe string to display.
41
43
  #
42
44
  # @param [String,#to_s] str object to be converted to a UTF-8 string
43
45
  # @param [Boolean] binary whether to expect binary data in the `str` parameter
@@ -8,6 +8,11 @@ module Datadog
8
8
  module Async
9
9
  # Adds threading behavior to workers
10
10
  # to run tasks asynchronously.
11
+ #
12
+ # This module is included in Polling module, and has no other
13
+ # direct users.
14
+ #
15
+ # @api private
11
16
  module Thread
12
17
  FORK_POLICY_STOP = :stop
13
18
  FORK_POLICY_RESTART = :restart
@@ -24,7 +29,11 @@ module Datadog
24
29
  # Methods that must be prepended
25
30
  module PrependedMethods
26
31
  def perform(*args)
27
- start_async { self.result = super(*args) } unless started?
32
+ unless started?
33
+ start_async do
34
+ self.result = super(*args)
35
+ end
36
+ end
28
37
  end
29
38
  end
30
39
 
@@ -5,6 +5,11 @@ module Datadog
5
5
  module Workers
6
6
  # Adds looping behavior to workers, with a sleep
7
7
  # interval between each loop.
8
+ #
9
+ # This module is included in Polling module, and has no other
10
+ # direct users.
11
+ #
12
+ # @api private
8
13
  module IntervalLoop
9
14
  BACK_OFF_RATIO = 1.2
10
15
  BACK_OFF_MAX = 5
@@ -38,15 +43,39 @@ module Datadog
38
43
 
39
44
  def stop_loop
40
45
  mutex.synchronize do
41
- return false unless run_loop?
42
-
46
+ # Do not call run_loop? from this method to see if the loop
47
+ # is running, because @run_loop is normally initialized by
48
+ # the background thread and if the stop is requested right
49
+ # after the worker starts, the background thread may be created
50
+ # (and scheduled) but hasn't run yet, thus skipping the
51
+ # write to @run_loop here would leave the thread running forever.
43
52
  @run_loop = false
53
+
54
+ # It is possible that we don't need to signal shutdown if
55
+ # @run_loop was not initialized (i.e. we changed it from not
56
+ # defined to false above). But let's be safe and signal the
57
+ # shutdown anyway, I don't see what harm it can cause.
44
58
  shutdown.signal
45
59
  end
46
60
 
61
+ # Previously, this method would return false (and do nothing)
62
+ # if the worker was not running the loop. However, this was racy -
63
+ # see https://github.com/DataDog/ruby-guild/issues/279.
64
+ # stop_loop now always sets the state to "stop requested" and,
65
+ # correspondingly, always returns true.
66
+ #
67
+ # There is some test code that returns false when mocking this
68
+ # method - most likely this method should be treated as a void one
69
+ # and the caller should assume that the stop was always requested.
47
70
  true
48
71
  end
49
72
 
73
+ # TODO This overwrites Queue's +work_pending?+ method with an
74
+ # implementation that, to me, is at leat questionable semantically:
75
+ # the Queue's idea of pending work is if the buffer is not empty,
76
+ # but this module says that work is pending if the work processing
77
+ # loop is scheduled to run (in other words, as long as the background
78
+ # thread is running, there is always pending work).
50
79
  def work_pending?
51
80
  run_loop?
52
81
  end
@@ -104,7 +133,19 @@ module Datadog
104
133
 
105
134
  def perform_loop
106
135
  mutex.synchronize do
107
- @run_loop = true
136
+ unless defined?(@run_loop)
137
+ # This write must only happen if @run_loop is not defined
138
+ # (i.e., not initialized). In the case when the worker is
139
+ # asked to stop right after it is created, the thread may not
140
+ # have run yet by the time +stop_loop+ is invoked and
141
+ # we need to preserve the stop-requested state from
142
+ # +stop_loop+ to +perform_loop+.
143
+ #
144
+ # If the workers are refactored to use classes and inheritance
145
+ # and their state, such as @run_loop, is initialized in
146
+ # constructors, the write can be made unconditional.
147
+ @run_loop = true
148
+ end
108
149
 
109
150
  shutdown.wait(mutex, loop_wait_time) if loop_wait_before_first_iteration?
110
151
  end
@@ -7,6 +7,8 @@ module Datadog
7
7
  module Core
8
8
  module Workers
9
9
  # Adds polling (async looping) behavior to workers
10
+ #
11
+ # @api private
10
12
  module Polling
11
13
  DEFAULT_SHUTDOWN_TIMEOUT = 1
12
14
 
@@ -5,6 +5,17 @@ module Datadog
5
5
  module Workers
6
6
  # Adds queue behavior to workers, with a buffer
7
7
  # to which items can be queued then dequeued.
8
+ #
9
+ # This module is included in some but not all workers.
10
+ # Notably, Data Streams Processor uses a queue but implements it
11
+ # inline rather than using this module.
12
+ #
13
+ # The workers that do include Queue also include Polling, which
14
+ # in turn includes Async::Thread and IntervalLoop. This means
15
+ # we have e.g. +in_iteration?+ always available in any worker
16
+ # that includes Queue.
17
+ #
18
+ # @api private
8
19
  module Queue
9
20
  def self.included(base)
10
21
  base.prepend(PrependedMethods)
@@ -13,11 +24,16 @@ module Datadog
13
24
  # Methods that must be prepended
14
25
  module PrependedMethods
15
26
  def perform(*args)
16
- super(*dequeue) if work_pending?
27
+ if work_pending?
28
+ work = dequeue
29
+ super(*work)
30
+ end
17
31
  end
18
32
  end
19
33
 
20
34
  def buffer
35
+ # Why is this an unsynchronized Array and not a Core::Buffer
36
+ # instance?
21
37
  @buffer ||= []
22
38
  end
23
39
 
@@ -34,10 +50,93 @@ module Datadog
34
50
  !buffer.empty?
35
51
  end
36
52
 
53
+ # Wait for the worker to finish handling all work that has already
54
+ # been submitted to it.
55
+ #
56
+ # If the worker is not enabled, returns nil.
57
+ # If the worker is enabled, returns whether, at the point of return,
58
+ # there was no pending or in progress work.
59
+ #
60
+ # Flushing can time out because there is a constant stream of work
61
+ # submitted at the same or higher rate than it is processed.
62
+ # Flushing can also fail if the worker thread is not running -
63
+ # this method will not flush from the calling thread.
64
+ def flush(timeout: nil)
65
+ # Default timeout is 5 seconds.
66
+ # Specific workers can override it to be more or less
67
+ timeout ||= 5
68
+
69
+ # Nothing needs to be done if the worker is not enabled.
70
+ return nil unless enabled?
71
+
72
+ unless running?
73
+ unless buffer.empty?
74
+ # If we are asked to flush but the worker is not running,
75
+ # do not flush from the caller thread. If the buffer is not
76
+ # empty, it will not be flushed. Log a warning to this effect.
77
+ #
78
+ # We are not guaranteed to have a logger as an instance method,
79
+ # reference the global for now - all other worker methods
80
+ # also reference the logger globally.
81
+ # TODO inject it into worker instances.
82
+ Datadog.logger.debug { "Asked to flush #{self} when the worker is not running" }
83
+ return false
84
+ end
85
+ end
86
+
87
+ started = Utils::Time.get_time
88
+ loop do
89
+ # The AppStarted event is triggered by the worker itself,
90
+ # from the worker thread. As such the main thread has no way
91
+ # to delay itself until that event is queued and we need some
92
+ # way to wait until that event is sent out to assert on it in
93
+ # the test suite. Check the run once flag which *should*
94
+ # indicate the event has been queued (at which point our queue
95
+ # depth check should wait until it's sent).
96
+ # This is still a hack because the flag can be overridden
97
+ # either way with or without the event being sent out.
98
+ # Note that if the AppStarted sending fails, this check
99
+ # will return false and flushing will be blocked until the
100
+ # 15 second timeout.
101
+ # Note that the first wait interval between telemetry event
102
+ # sending is 10 seconds, the timeout needs to be strictly
103
+ # greater than that.
104
+ return true if idle?
105
+
106
+ return false if Utils::Time.get_time - started > timeout
107
+
108
+ sleep 0.5
109
+ end
110
+ end
111
+
37
112
  protected
38
113
 
39
114
  attr_writer \
40
115
  :buffer
116
+
117
+ # Returns whether this worker has no pending work and is not actively
118
+ # working.
119
+ #
120
+ # The reason why "actively working" is considered is that we use
121
+ # flushing to ensure all work is completed before asserting on the
122
+ # outcome in the tests - if work is happening in a background thread,
123
+ # it's too early to assert on its results.
124
+ def idle?
125
+ # We have a +work_pending?+ method in this class that semantically
126
+ # would be appropriate here instead of calling +buffer.empty?+.
127
+ # Unfortunately IntervalLoop replaces our implementation of
128
+ # +work_pending?+ with one that doesn't make sense at least for the
129
+ # Queue. And we can't change the order of module includes because
130
+ # they all override +perform+ and the correct behavior depends on
131
+ # placing IntervalLoop after Queue.
132
+ #
133
+ # The TraceWriter worker then defines +work_pending?+ to be the
134
+ # same as Queue implementation here... Essentially, it demands
135
+ # the behavior that perhaps should be applied to all workers.
136
+ #
137
+ # Until this mess is untangled, call +buffer.empty?+ here.
138
+ buffer.empty? && !in_iteration?
139
+ end
41
140
  end
42
141
  end
43
142
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../core/environment/variable_helpers'
4
+ require_relative '../ext'
5
+
6
+ module Datadog
7
+ module DataStreams
8
+ module Configuration
9
+ # Configuration settings for Data Streams Monitoring.
10
+ module Settings
11
+ def self.extended(base)
12
+ base = base.singleton_class unless base.is_a?(Class)
13
+ add_settings!(base)
14
+ end
15
+
16
+ def self.add_settings!(base)
17
+ base.class_eval do
18
+ # Data Streams Monitoring configuration
19
+ # @public_api
20
+ settings :data_streams do
21
+ # Whether Data Streams Monitoring is enabled. When enabled, the library will
22
+ # collect and report data lineage information for messaging systems.
23
+ #
24
+ # @default `DD_DATA_STREAMS_ENABLED` environment variable, otherwise `false`.
25
+ # @return [Boolean]
26
+ option :enabled do |o|
27
+ o.type :bool
28
+ o.env Ext::ENV_ENABLED
29
+ o.default false
30
+ end
31
+
32
+ # The interval (in seconds) at which Data Streams Monitoring stats are flushed.
33
+ #
34
+ # @default 10.0
35
+ # @env '_DD_TRACE_STATS_WRITER_INTERVAL'
36
+ # @return [Float]
37
+ # @!visibility private
38
+ option :interval do |o|
39
+ o.type :float
40
+ o.env '_DD_TRACE_STATS_WRITER_INTERVAL'
41
+ o.default 10.0
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configuration/settings'
4
+
5
+ module Datadog
6
+ module DataStreams
7
+ # Configuration for Data Streams Monitoring
8
+ module Configuration
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module DataStreams
5
+ # Constants for Data Streams Monitoring configuration
6
+ # @public_api
7
+ module Ext
8
+ ENV_ENABLED = 'DD_DATA_STREAMS_ENABLED'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'configuration'
4
+ require_relative '../core/configuration'
5
+
6
+ module Datadog
7
+ module DataStreams
8
+ # Extends Datadog with Data Streams Monitoring features
9
+ module Extensions
10
+ # Inject Data Streams settings into global configuration.
11
+ def self.activate!
12
+ Core::Configuration::Settings.extend(Configuration::Settings)
13
+ end
14
+ end
15
+ end
16
+ end