statsd-instrument 3.9.9 → 3.9.10
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 +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +8 -0
- data/lib/statsd/instrument/aggregator.rb +10 -0
- data/lib/statsd/instrument/version.rb +1 -1
- data/lib/statsd/instrument.rb +1 -1
- data/test/aggregator_test.rb +46 -0
- data/test/dispatcher_stats_test.rb +6 -6
- data/test/integration_test.rb +52 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6bb36fa3a5254a64999335275c5515579febf9c2d92d35522fee1d883d0f56d
|
4
|
+
data.tar.gz: 69c96c4f5abaa8cae674a85e562a41ab3bbcf05cde0ef523c934d86846103658
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 321874ee863b3f66e1eb7abbace4e734af55dc86fbd11aa8e009e398c54414f47d91447cdcbf08fb93bd4f7b1d88d58a16217093e9f8978b400187ed513dbd74
|
7
|
+
data.tar.gz: 9f34eed89dee5efc7d9a3d800c1d6824b0396054782694d708dab40180e0c3b423f6a0c2337b995bf6f58d72ae750bed564cf02bb5e13fc08518540e443be400
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
3.
|
1
|
+
3.4.4
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,14 @@ section below.
|
|
6
6
|
|
7
7
|
## Unreleased changes
|
8
8
|
|
9
|
+
## Version 3.9.10
|
10
|
+
|
11
|
+
- [#398](https://github.com/Shopify/statsd-instrument/pull/398) - Fix metrics not being sent from signal trap contexts when aggregation is enabled.
|
12
|
+
When the aggregator is enabled and metrics are emitted from within a signal handler (e.g., SIGTERM, SIGINT),
|
13
|
+
the thread health check would fail with `ThreadError: can't be called from trap context` due to mutex
|
14
|
+
synchronization. The aggregator now gracefully falls back to direct writes when called from a trap context,
|
15
|
+
ensuring metrics are not lost during signal handling such as graceful shutdowns.
|
16
|
+
|
9
17
|
## Version 3.9.9
|
10
18
|
|
11
19
|
- [#392](https://github.com/Shopify/statsd-instrument/pull/392) - Prevent ENOBUFS errors when using UDP, by skipping setting socket buffer size.
|
@@ -278,6 +278,16 @@ module StatsD
|
|
278
278
|
end
|
279
279
|
true
|
280
280
|
end
|
281
|
+
rescue ThreadError => e
|
282
|
+
# If we're in a trap context, we can't use mutex synchronization
|
283
|
+
# Fall back to direct writes to avoid losing metrics
|
284
|
+
if e.message.include?("can't be called from trap context")
|
285
|
+
StatsD.logger.debug { "[#{self.class.name}] In trap context, falling back to direct writes" }
|
286
|
+
false
|
287
|
+
else
|
288
|
+
# Re-raise other ThreadErrors
|
289
|
+
raise
|
290
|
+
end
|
281
291
|
end
|
282
292
|
end
|
283
293
|
end
|
data/lib/statsd/instrument.rb
CHANGED
@@ -362,7 +362,7 @@ module StatsD
|
|
362
362
|
# @!method distribution(name, value = nil, sample_rate: nil, tags: nil, &block)
|
363
363
|
# (see StatsD::Instrument::Client#distribution)
|
364
364
|
#
|
365
|
-
# @!method event(title, text, tags: nil, hostname: nil, timestamp: nil, aggregation_key: nil, priority: nil, source_type_name: nil, alert_type: nil)
|
365
|
+
# @!method event(title, text, tags: nil, hostname: nil, timestamp: nil, aggregation_key: nil, priority: nil, source_type_name: nil, alert_type: nil)
|
366
366
|
# (see StatsD::Instrument::Client#event)
|
367
367
|
#
|
368
368
|
# @!method service_check(name, status, tags: nil, hostname: nil, timestamp: nil, message: nil)
|
data/test/aggregator_test.rb
CHANGED
@@ -353,4 +353,50 @@ class AggregatorTest < Minitest::Test
|
|
353
353
|
assert_equal(100.0, sampled_timing_datagram.value)
|
354
354
|
assert_equal(0.01, sampled_timing_datagram.sample_rate)
|
355
355
|
end
|
356
|
+
|
357
|
+
def test_signal_trap_context_fallback_to_direct_writes
|
358
|
+
skip("#{RUBY_ENGINE} not supported for this test. Reason: signal handling") if RUBY_ENGINE != "ruby"
|
359
|
+
|
360
|
+
signal_received = false
|
361
|
+
metrics_sent_in_trap = []
|
362
|
+
|
363
|
+
old_trap = Signal.trap("USR1") do
|
364
|
+
signal_received = true
|
365
|
+
# These operations should now fall back to direct writes
|
366
|
+
@subject.increment("trap_counter", 1)
|
367
|
+
@subject.gauge("trap_gauge", 42)
|
368
|
+
@subject.aggregate_timing("trap_timing", 100)
|
369
|
+
|
370
|
+
metrics_sent_in_trap = @sink.datagrams.map(&:name)
|
371
|
+
end
|
372
|
+
|
373
|
+
@sink.clear
|
374
|
+
|
375
|
+
Process.kill("USR1", Process.pid)
|
376
|
+
|
377
|
+
sleep(0.1)
|
378
|
+
|
379
|
+
assert(signal_received, "Signal should have been received")
|
380
|
+
|
381
|
+
assert_includes(metrics_sent_in_trap, "trap_counter")
|
382
|
+
assert_includes(metrics_sent_in_trap, "trap_gauge")
|
383
|
+
assert_includes(metrics_sent_in_trap, "trap_timing")
|
384
|
+
|
385
|
+
counter_datagram = @sink.datagrams.find { |d| d.name == "trap_counter" }
|
386
|
+
assert_equal(1, counter_datagram.value)
|
387
|
+
|
388
|
+
gauge_datagram = @sink.datagrams.find { |d| d.name == "trap_gauge" }
|
389
|
+
assert_equal(42, gauge_datagram.value)
|
390
|
+
|
391
|
+
timing_datagram = @sink.datagrams.find { |d| d.name == "trap_timing" }
|
392
|
+
assert_equal([100.0], [timing_datagram.value].flatten)
|
393
|
+
|
394
|
+
debug_messages = @logger.messages.select { |m| m[:severity] == :debug }
|
395
|
+
assert(
|
396
|
+
debug_messages.any? { |m| m[:message].include?("In trap context, falling back to direct writes") },
|
397
|
+
"Expected debug message about trap context fallback",
|
398
|
+
)
|
399
|
+
ensure
|
400
|
+
Signal.trap("USR1", old_trap || "DEFAULT")
|
401
|
+
end
|
356
402
|
end
|
@@ -48,21 +48,21 @@ class DispatcherStatsTest < Minitest::Test
|
|
48
48
|
end
|
49
49
|
assert_equal(batches.length, stats.instance_variable_get(:@batched_sends))
|
50
50
|
assert_equal(
|
51
|
-
batches.map
|
51
|
+
batches.map do |b|
|
52
52
|
b[:buffer_len]
|
53
|
-
|
53
|
+
end.sum / batches.length,
|
54
54
|
stats.instance_variable_get(:@avg_buffer_length),
|
55
55
|
)
|
56
56
|
assert_equal(
|
57
|
-
batches.map
|
57
|
+
batches.map do |b|
|
58
58
|
b[:packet_size]
|
59
|
-
|
59
|
+
end.sum / batches.length,
|
60
60
|
stats.instance_variable_get(:@avg_batched_packet_size),
|
61
61
|
)
|
62
62
|
assert_equal(
|
63
|
-
batches.map
|
63
|
+
batches.map do |b|
|
64
64
|
b[:batch_len]
|
65
|
-
|
65
|
+
end.sum / batches.length,
|
66
66
|
stats.instance_variable_get(:@avg_batch_length),
|
67
67
|
)
|
68
68
|
end
|
data/test/integration_test.rb
CHANGED
@@ -103,4 +103,56 @@ class IntegrationTest < Minitest::Test
|
|
103
103
|
assert_match(/counter:\d+|c/, packets.find { |packet| packet.start_with?("counter:") })
|
104
104
|
assert_match(/test_distribution:\d+:3|d/, packets.find { |packet| packet.start_with?("test_distribution:") })
|
105
105
|
end
|
106
|
+
|
107
|
+
def test_signal_trap_with_aggregation_fallback
|
108
|
+
skip("#{RUBY_ENGINE} not supported for this test. Reason: signal handling") if RUBY_ENGINE != "ruby"
|
109
|
+
|
110
|
+
client = StatsD::Instrument::Environment.new(
|
111
|
+
"STATSD_ADDR" => "#{@server.addr[2]}:#{@server.addr[1]}",
|
112
|
+
"STATSD_IMPLEMENTATION" => "dogstatsd",
|
113
|
+
"STATSD_ENV" => "production",
|
114
|
+
"STATSD_ENABLE_AGGREGATION" => "true",
|
115
|
+
"STATSD_AGGREGATION_INTERVAL" => "5.0",
|
116
|
+
).client
|
117
|
+
|
118
|
+
signal_received = false
|
119
|
+
|
120
|
+
old_trap = Signal.trap("USR1") do
|
121
|
+
signal_received = true
|
122
|
+
# These should fall back to direct writes
|
123
|
+
client.increment("trap_metric", 5)
|
124
|
+
client.gauge("trap_gauge", 42)
|
125
|
+
client.distribution("trap_distribution", 100)
|
126
|
+
end
|
127
|
+
|
128
|
+
Process.kill("USR1", Process.pid)
|
129
|
+
|
130
|
+
sleep(0.1)
|
131
|
+
|
132
|
+
assert(signal_received, "Signal should have been received")
|
133
|
+
|
134
|
+
packets = []
|
135
|
+
while IO.select([@server], nil, nil, 0.1)
|
136
|
+
packet = @server.recvfrom(300).first
|
137
|
+
packets.concat(packet.split("\n"))
|
138
|
+
end
|
139
|
+
|
140
|
+
# When aggregation is disabled due to trap context, metrics might be batched
|
141
|
+
assert(packets.size >= 3, "Expected at least 3 metrics, got #{packets.size}: #{packets.inspect}")
|
142
|
+
|
143
|
+
assert(
|
144
|
+
packets.any? { |p| p == "trap_metric:5|c" },
|
145
|
+
"Expected counter metric, got: #{packets.inspect}",
|
146
|
+
)
|
147
|
+
assert(
|
148
|
+
packets.any? { |p| p == "trap_gauge:42|g" },
|
149
|
+
"Expected gauge metric, got: #{packets.inspect}",
|
150
|
+
)
|
151
|
+
assert(
|
152
|
+
packets.any? { |p| p == "trap_distribution:100|d" },
|
153
|
+
"Expected distribution metric, got: #{packets.inspect}",
|
154
|
+
)
|
155
|
+
ensure
|
156
|
+
Signal.trap("USR1", old_trap || "DEFAULT")
|
157
|
+
end
|
106
158
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: statsd-instrument
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.9.
|
4
|
+
version: 3.9.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jesse Storimer
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
- Willem van Bergen
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: A StatsD client for Ruby apps. Provides metaprogramming methods to inject
|
15
15
|
StatsD instrumentation into your code.
|
@@ -130,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '0'
|
132
132
|
requirements: []
|
133
|
-
rubygems_version: 3.6.
|
133
|
+
rubygems_version: 3.6.9
|
134
134
|
specification_version: 4
|
135
135
|
summary: A StatsD client for Ruby apps
|
136
136
|
test_files:
|