dogstatsd-ruby 5.5.0 → 5.6.0

Sign up to get free protection for your applications and to get access to all the features.
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