dogstatsd-ruby 5.5.0 → 5.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92868c3d0ccb9a7847eaec3dd2da46413ab8acd2ce2df0008ee66c3e6500f3f6
4
- data.tar.gz: 26064f00534e7df4a132d722041eea6b85a22849ca5c3c7caed05722320bcd9e
3
+ metadata.gz: 20363c18876c0641ab23a8cee8d28c22de3e4210420e398d639d6a7b384da168
4
+ data.tar.gz: 851d63e75192dd81451f7a94abba7efe5c81e9c4afdc2dce8102a76e44a10d9a
5
5
  SHA512:
6
- metadata.gz: e9f076b5aa76f4102f66698d5d19a3fbcd84e2b8b3a5fa944dcac07ddf8ee25f48a65d90e5296cbd7327f04dc90a8aba0234606bc50ab7c335120e9e17c3a6d2
7
- data.tar.gz: e07f4469b7737fe7d67d32623194c90e09adf3d78ef0c26226bfed103db4067fa01e582f0627d426695dd9a0ff93c4e1e7d79d227bd5ce806d9ca28b2a440382
6
+ metadata.gz: 72f565700a7ddf32ca56a686617e840c53ad167770be812a66ad6af5d1641c96f89796fe147c1565c38c7d83be14fc638e0f8ce76a00e62e980f43a510826d18
7
+ data.tar.gz: 659bbd4b5b669a11bf62f723387d0263d76a243bda5c6b5de101700b20ad2991d6fd6535832857acdd15b0f809faff80cb1e9f459e1ddacb3262a6be27c4785f
data/README.md CHANGED
@@ -183,7 +183,7 @@ There is also an implicit message which closes the queue which will cause the se
183
183
  statsd = Datadog::Statsd.new('localhost', 8125)
184
184
  ```
185
185
 
186
- The message queue's maximum size (in messages) is given by the `sender_queue_size` argument, and has appropriate defaults for UDP (2048) and UDS (512).
186
+ The message queue's maximum size (in messages) is given by the `sender_queue_size` argument, and has appropriate defaults for UDP (2048), UDS (512) and `single_thread: true` (1).
187
187
 
188
188
  The `buffer_flush_interval`, if enabled, is implemented with an additional thread which manages the timing of those flushes. This additional thread is used even if `single_thread: true`.
189
189
 
@@ -209,6 +209,16 @@ By default, instances of `Datadog::Statsd` are thread-safe and we recommend that
209
209
 
210
210
  When using the `single_thread: true` mode, instances of `Datadog::Statsd` are still thread-safe, but you may run into contention on heavily-threaded applications, so we don’t recommend (for performance reasons) reusing these instances.
211
211
 
212
+ ### Delaying serialization
213
+
214
+ By default, message serialization happens synchronously whenever stat methods such as `#increment` gets called, blocking the caller. If the blocking is impacting your program's performance, you may want to consider the `delay_serialization: true` mode.
215
+
216
+ The `delay_serialization: true` mode delays the serialization of metrics to avoid the wait when submitting metrics. Serialization will still have to happen at some point, but it might be postponed until a more convenient time, such as after an HTTP request has completed.
217
+
218
+ In `single_thread: true` mode, you'll probably want to set `sender_queue_size:` from it's default of `1` to some greater value, so that it can benefit from `delay_serialization: true`. Messages will then be queued unserialized in the sender queue and processed normally whenever `sender_queue_size` is reached or `#flush` is called. You might set `sender_queue_size: Float::INFINITY` to allow for an unbounded queue that will only be processed on explicit `#flush`.
219
+
220
+ In `single_thread: false` mode, `delay_serialization: true`, will cause serialization to happen inside the sender thread.
221
+
212
222
  ## Versioning
213
223
 
214
224
  This Ruby gem is using [Semantic Versioning](https://guides.rubygems.org/patterns/#semantic-versioning) but please note that supported Ruby versions can change in a minor release of this library.
@@ -9,7 +9,7 @@ module Datadog
9
9
  end
10
10
 
11
11
  def reset_telemetry
12
- telemetry.reset
12
+ telemetry.reset if telemetry
13
13
  end
14
14
 
15
15
  # not thread safe: `Sender` instances that use this are required to properly synchronize or sequence calls to this method
@@ -23,12 +23,25 @@ module Datadog
23
23
 
24
24
  private
25
25
 
26
+ ERROR_MESSAGE = "Valid environment variables combination for connection configuration:\n" +
27
+ " - DD_DOGSTATSD_URL for UDP or UDS connection.\n" +
28
+ " Example for UDP: DD_DOGSTATSD_URL='udp://localhost:8125'\n" +
29
+ " Example for UDS: DD_DOGSTATSD_URL='unix:///path/to/unix.sock'\n" +
30
+ " or\n" +
31
+ " - DD_AGENT_HOST and DD_DOGSTATSD_PORT for an UDP connection. E.g. DD_AGENT_HOST='localhost' DD_DOGSTATSD_PORT=8125\n" +
32
+ " or\n" +
33
+ " - DD_DOGSTATSD_SOCKET for an UDS connection: E.g. DD_DOGSTATSD_SOCKET='/path/to/unix.sock'\n" +
34
+ " Note that DD_DOGSTATSD_URL has priority on other environment variables."
35
+
26
36
  DEFAULT_HOST = '127.0.0.1'
27
37
  DEFAULT_PORT = 8125
28
38
 
39
+ UDP_PREFIX = 'udp://'
40
+ UDS_PREFIX = 'unix://'
41
+
29
42
  def initialize_with_constructor_args(host: nil, port: nil, socket_path: nil)
30
43
  try_initialize_with(host: host, port: port, socket_path: socket_path,
31
- not_both_error_message:
44
+ error_message:
32
45
  "Both UDP: (host/port #{host}:#{port}) and UDS (socket_path #{socket_path}) " +
33
46
  "constructor arguments were given. Use only one or the other.",
34
47
  )
@@ -36,13 +49,11 @@ module Datadog
36
49
 
37
50
  def initialize_with_env_vars()
38
51
  try_initialize_with(
52
+ dogstatsd_url: ENV['DD_DOGSTATSD_URL'],
39
53
  host: ENV['DD_AGENT_HOST'],
40
54
  port: ENV['DD_DOGSTATSD_PORT'] && ENV['DD_DOGSTATSD_PORT'].to_i,
41
55
  socket_path: ENV['DD_DOGSTATSD_SOCKET'],
42
- not_both_error_message:
43
- "Both UDP (DD_AGENT_HOST/DD_DOGSTATSD_PORT #{ENV['DD_AGENT_HOST']}:#{ENV['DD_DOGSTATSD_PORT']}) " +
44
- "and UDS (DD_DOGSTATSD_SOCKET #{ENV['DD_DOGSTATSD_SOCKET']}) environment variables are set. " +
45
- "Set only one or the other.",
56
+ error_message: ERROR_MESSAGE,
46
57
  )
47
58
  end
48
59
 
@@ -50,9 +61,13 @@ module Datadog
50
61
  try_initialize_with(host: DEFAULT_HOST, port: DEFAULT_PORT)
51
62
  end
52
63
 
53
- def try_initialize_with(host: nil, port: nil, socket_path: nil, not_both_error_message: "")
64
+ def try_initialize_with(dogstatsd_url: nil, host: nil, port: nil, socket_path: nil, error_message: ERROR_MESSAGE)
54
65
  if (host || port) && socket_path
55
- raise ArgumentError, not_both_error_message
66
+ raise ArgumentError, error_message
67
+ end
68
+
69
+ if dogstatsd_url
70
+ host, port, socket_path = parse_dogstatsd_url(str: dogstatsd_url.to_s)
56
71
  end
57
72
 
58
73
  if host || port
@@ -71,6 +86,40 @@ module Datadog
71
86
 
72
87
  return false
73
88
  end
89
+
90
+ def parse_dogstatsd_url(str:)
91
+ # udp socket connection
92
+
93
+ if str.start_with?(UDP_PREFIX)
94
+ dogstatsd_url = str[UDP_PREFIX.size..str.size]
95
+ host = nil
96
+ port = nil
97
+
98
+ if dogstatsd_url.include?(":")
99
+ parts = dogstatsd_url.split(":")
100
+ if parts.size > 2
101
+ raise ArgumentError, "Error: DD_DOGSTATSD_URL wrong format for an UDP connection. E.g. 'udp://localhost:8125'"
102
+ end
103
+
104
+ host = parts[0]
105
+ port = parts[1].to_i
106
+ else
107
+ host = dogstatsd_url
108
+ end
109
+
110
+ return host, port, nil
111
+ end
112
+
113
+ # unix socket connection
114
+
115
+ if str.start_with?(UDS_PREFIX)
116
+ return nil, nil, str[UDS_PREFIX.size..str.size]
117
+ end
118
+
119
+ # malformed value
120
+
121
+ raise ArgumentError, "Error: DD_DOGSTATSD_URL has been provided but is not starting with 'udp://' nor 'unix://'"
122
+ end
74
123
  end
75
124
  end
76
125
  end
@@ -21,7 +21,9 @@ module Datadog
21
21
 
22
22
  single_thread: false,
23
23
 
24
- logger: nil
24
+ logger: nil,
25
+
26
+ serializer:
25
27
  )
26
28
  @transport_type = connection_cfg.transport_type
27
29
 
@@ -52,8 +54,10 @@ module Datadog
52
54
  max_payload_size: buffer_max_payload_size,
53
55
  max_pool_size: buffer_max_pool_size || DEFAULT_BUFFER_POOL_SIZE,
54
56
  overflowing_stategy: buffer_overflowing_stategy,
57
+ serializer: serializer
55
58
  )
56
59
 
60
+ sender_queue_size ||= 1 if single_thread
57
61
  sender_queue_size ||= (@transport_type == :udp ?
58
62
  UDP_DEFAULT_SENDER_QUEUE_SIZE : UDS_DEFAULT_SENDER_QUEUE_SIZE)
59
63
 
@@ -61,7 +65,8 @@ module Datadog
61
65
  SingleThreadSender.new(
62
66
  buffer,
63
67
  logger: logger,
64
- flush_interval: buffer_flush_interval) :
68
+ flush_interval: buffer_flush_interval,
69
+ queue_size: sender_queue_size) :
65
70
  Sender.new(
66
71
  buffer,
67
72
  logger: logger,
@@ -8,7 +8,8 @@ module Datadog
8
8
  def initialize(connection,
9
9
  max_payload_size: nil,
10
10
  max_pool_size: DEFAULT_BUFFER_POOL_SIZE,
11
- overflowing_stategy: :drop
11
+ overflowing_stategy: :drop,
12
+ serializer:
12
13
  )
13
14
  raise ArgumentError, 'max_payload_size keyword argument must be provided' unless max_payload_size
14
15
  raise ArgumentError, 'max_pool_size keyword argument must be provided' unless max_pool_size
@@ -17,12 +18,19 @@ module Datadog
17
18
  @max_payload_size = max_payload_size
18
19
  @max_pool_size = max_pool_size
19
20
  @overflowing_stategy = overflowing_stategy
21
+ @serializer = serializer
20
22
 
21
23
  @buffer = String.new
22
24
  clear_buffer
23
25
  end
24
26
 
25
27
  def add(message)
28
+ # Serializes the message if it hasn't been already. Part of the
29
+ # delay_serialization feature.
30
+ if message.is_a?(Array)
31
+ message = @serializer.to_stat(*message[0], **message[1])
32
+ end
33
+
26
34
  message_size = message.bytesize
27
35
 
28
36
  return nil unless message_size > 0 # to avoid adding empty messages to the buffer
@@ -84,7 +84,10 @@ module Datadog
84
84
  if message_queue.length <= @queue_size
85
85
  message_queue << message
86
86
  else
87
- @telemetry.dropped_queue(packets: 1, bytes: message.bytesize) if @telemetry
87
+ if @telemetry
88
+ bytesize = message.respond_to?(:bytesize) ? message.bytesize : 0
89
+ @telemetry.dropped_queue(packets: 1, bytes: bytesize)
90
+ end
88
91
  end
89
92
  end
90
93
 
@@ -7,10 +7,12 @@ module Datadog
7
7
  # It is using current Process.PID to check it is the result of a recent fork
8
8
  # and it is reseting the MessageBuffer if that's the case.
9
9
  class SingleThreadSender
10
- def initialize(message_buffer, logger: nil, flush_interval: nil)
10
+ def initialize(message_buffer, logger: nil, flush_interval: nil, queue_size: 1)
11
11
  @message_buffer = message_buffer
12
12
  @logger = logger
13
13
  @mx = Mutex.new
14
+ @message_queue_size = queue_size
15
+ @message_queue = []
14
16
  @flush_timer = if flush_interval
15
17
  Datadog::Statsd::Timer.new(flush_interval) { flush }
16
18
  else
@@ -26,15 +28,21 @@ module Datadog
26
28
  # not send, they belong to the parent process, let's clear the buffer.
27
29
  if forked?
28
30
  @message_buffer.reset
31
+ @message_queue.clear
29
32
  @flush_timer.start if @flush_timer && @flush_timer.stop?
30
33
  update_fork_pid
31
34
  end
32
- @message_buffer.add(message)
35
+
36
+ @message_queue << message
37
+ if @message_queue.size >= @message_queue_size
38
+ drain_message_queue
39
+ end
33
40
  }
34
41
  end
35
42
 
36
43
  def flush(*)
37
44
  @mx.synchronize {
45
+ drain_message_queue
38
46
  @message_buffer.flush()
39
47
  }
40
48
  end
@@ -53,6 +61,12 @@ module Datadog
53
61
 
54
62
  private
55
63
 
64
+ def drain_message_queue
65
+ while msg = @message_queue.shift
66
+ @message_buffer.add(msg)
67
+ end
68
+ end
69
+
56
70
  # below are "fork management" methods to be able to clean the MessageBuffer
57
71
  # if it detects that it is running in a unknown PID.
58
72
 
@@ -4,6 +4,6 @@ require_relative 'connection'
4
4
 
5
5
  module Datadog
6
6
  class Statsd
7
- VERSION = '5.5.0'
7
+ VERSION = '5.6.0'
8
8
  end
9
9
  end
@@ -76,11 +76,12 @@ module Datadog
76
76
  # @option [Logger] logger for debugging
77
77
  # @option [Integer] buffer_max_payload_size max bytes to buffer
78
78
  # @option [Integer] buffer_max_pool_size max messages to buffer
79
- # @option [Integer] sender_queue_size size of the sender queue in number of buffers (multi-thread only)
79
+ # @option [Integer] sender_queue_size size of the sender queue in number of buffers
80
80
  # @option [Numeric] buffer_flush_interval interval in second to flush buffer
81
81
  # @option [String] socket_path unix socket path
82
82
  # @option [Float] default sample rate if not overridden
83
83
  # @option [Boolean] single_thread flushes the metrics on the main thread instead of in a companion thread
84
+ # @option [Boolean] delay_serialization delays stat serialization
84
85
  def initialize(
85
86
  host = nil,
86
87
  port = nil,
@@ -100,6 +101,7 @@ module Datadog
100
101
  logger: nil,
101
102
 
102
103
  single_thread: false,
104
+ delay_serialization: false,
103
105
 
104
106
  telemetry_enable: true,
105
107
  telemetry_flush_interval: DEFAULT_TELEMETRY_FLUSH_INTERVAL
@@ -112,6 +114,7 @@ module Datadog
112
114
  @prefix = @namespace ? "#{@namespace}.".freeze : nil
113
115
  @serializer = Serialization::Serializer.new(prefix: @prefix, global_tags: tags)
114
116
  @sample_rate = sample_rate
117
+ @delay_serialization = delay_serialization
115
118
 
116
119
  @forwarder = Forwarder.new(
117
120
  connection_cfg: ConnectionCfg.new(
@@ -133,6 +136,7 @@ module Datadog
133
136
  sender_queue_size: sender_queue_size,
134
137
 
135
138
  telemetry_flush_interval: telemetry_enable ? telemetry_flush_interval : nil,
139
+ serializer: serializer
136
140
  )
137
141
  end
138
142
 
@@ -425,7 +429,12 @@ module Datadog
425
429
  sample_rate = opts[:sample_rate] || @sample_rate || 1
426
430
 
427
431
  if sample_rate == 1 || opts[:pre_sampled] || rand <= sample_rate
428
- full_stat = serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
432
+ full_stat =
433
+ if @delay_serialization
434
+ [[stat, delta, type], {tags: opts[:tags], sample_rate: sample_rate}]
435
+ else
436
+ serializer.to_stat(stat, delta, type, tags: opts[:tags], sample_rate: sample_rate)
437
+ end
429
438
 
430
439
  forwarder.send_message(full_stat)
431
440
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dogstatsd-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.0
4
+ version: 5.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rein Henrichs
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-06-01 00:00:00.000000000 Z
12
+ date: 2023-07-10 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: A Ruby DogStatsd client
15
15
  email: code@datadoghq.com
@@ -44,9 +44,9 @@ licenses:
44
44
  - MIT
45
45
  metadata:
46
46
  bug_tracker_uri: https://github.com/DataDog/dogstatsd-ruby/issues
47
- changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/v5.5.0/CHANGELOG.md
48
- documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/5.5.0
49
- source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/v5.5.0
47
+ changelog_uri: https://github.com/DataDog/dogstatsd-ruby/blob/v5.6.0/CHANGELOG.md
48
+ documentation_uri: https://www.rubydoc.info/gems/dogstatsd-ruby/5.6.0
49
+ source_code_uri: https://github.com/DataDog/dogstatsd-ruby/tree/v5.6.0
50
50
  post_install_message: |2+
51
51
 
52
52
  If you are upgrading from v4.x of the dogstatsd-ruby library, note the major change to the threading model:
@@ -66,8 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0'
68
68
  requirements: []
69
- rubyforge_project:
70
- rubygems_version: 2.7.10
69
+ rubygems_version: 3.2.3
71
70
  signing_key:
72
71
  specification_version: 4
73
72
  summary: A Ruby DogStatsd client