io-metrics 0.2.0 → 0.3.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/io/metrics/listener/darwin.rb +6 -4
- data/lib/io/metrics/listener/linux.rb +37 -12
- data/lib/io/metrics/listener.rb +8 -6
- data/lib/io/metrics/version.rb +1 -1
- data/readme.md +10 -0
- data/releases.md +10 -0
- data.tar.gz.sig +0 -0
- metadata +1 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f7f07281a060f8644b5f50c62d526bfca3ea15ecb8a63001192f617b0aa7690d
|
|
4
|
+
data.tar.gz: ae6e4568aeae519c4e69643cc60a9203e0549408db787148b035151462a12819
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 69bc0a438888c3f4a58790d53aa09919110e7c538c6cbdbf04165c2c6472043a47844b5b61a5d8db61df739289ea806c7f906422c734a0a3a0f3c727b7756624
|
|
7
|
+
data.tar.gz: a3a052adb78a2d63276282795ffc9c2d3d691f5afc7dbc2313a9fe528a1f4ee5d82157b050aef6514d52b6cf8025ff4c3344d59df41782653916bbc3491069c2
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -83,10 +83,12 @@ class IO
|
|
|
83
83
|
# Apply filter if specified
|
|
84
84
|
next if address_filter && !address_filter.include?(key.downcase)
|
|
85
85
|
|
|
86
|
-
listeners[key] ||= Listener.new(addrinfo, 0, 0)
|
|
87
|
-
listeners[key].
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
listeners[key] ||= Listener.new(addrinfo, 0, 0, 0)
|
|
87
|
+
listeners[key].queued_count = queue_length
|
|
88
|
+
|
|
89
|
+
# active_count and close_wait_count set to 0 (netstat -L doesn't expose connection states)
|
|
90
|
+
listeners[key].active_count = 0
|
|
91
|
+
listeners[key].close_wait_count = 0
|
|
90
92
|
end
|
|
91
93
|
end
|
|
92
94
|
end
|
|
@@ -161,8 +161,9 @@ class IO
|
|
|
161
161
|
listeners = {}
|
|
162
162
|
address_filter = addresses ? addresses.map{|address| address.downcase}.to_set : nil
|
|
163
163
|
connections = []
|
|
164
|
+
close_wait_connections = []
|
|
164
165
|
|
|
165
|
-
# Single pass: collect LISTEN sockets and
|
|
166
|
+
# Single pass: collect LISTEN sockets, ESTABLISHED, and CLOSE_WAIT connections
|
|
166
167
|
File.foreach(file) do |line|
|
|
167
168
|
next if line.start_with?("sl")
|
|
168
169
|
|
|
@@ -192,12 +193,14 @@ class IO
|
|
|
192
193
|
# Apply filter if specified
|
|
193
194
|
next if address_filter && !address_filter.include?(local_address.downcase)
|
|
194
195
|
|
|
195
|
-
listeners[local_address] ||= Listener.new(Addrinfo.tcp(local_ip, local_port), 0, 0)
|
|
196
|
-
# rx_queue shows number of connections waiting to be accepted
|
|
197
|
-
|
|
198
|
-
listeners[local_address].
|
|
199
|
-
|
|
200
|
-
|
|
196
|
+
listeners[local_address] ||= Listener.new(Addrinfo.tcp(local_ip, local_port), 0, 0, 0)
|
|
197
|
+
# rx_queue shows number of connections waiting to be accepted.
|
|
198
|
+
# Accumulate across SO_REUSEPORT sockets sharing the same address.
|
|
199
|
+
listeners[local_address].queued_count += rx_queue_hex.to_i(16)
|
|
200
|
+
listeners[local_address].active_count = 0
|
|
201
|
+
listeners[local_address].close_wait_count = 0
|
|
202
|
+
# Collect ESTABLISHED and CLOSE_WAIT connections to count later
|
|
203
|
+
elsif state == :established || state == :close_wait
|
|
201
204
|
if ipv6
|
|
202
205
|
local_ip = parse_ipv6(local_ip_hex)
|
|
203
206
|
local_address = "[#{local_ip}]:#{parse_port(local_port_hex)}"
|
|
@@ -206,7 +209,11 @@ class IO
|
|
|
206
209
|
local_port = parse_port(local_port_hex)
|
|
207
210
|
local_address = "#{local_ip}:#{local_port}"
|
|
208
211
|
end
|
|
209
|
-
|
|
212
|
+
if state == :established
|
|
213
|
+
connections << local_address
|
|
214
|
+
else
|
|
215
|
+
close_wait_connections << local_address
|
|
216
|
+
end
|
|
210
217
|
end
|
|
211
218
|
end
|
|
212
219
|
end
|
|
@@ -214,10 +221,28 @@ class IO
|
|
|
214
221
|
# Count ESTABLISHED connections for each listener
|
|
215
222
|
connections.each do |local_address|
|
|
216
223
|
if listener_address = find_matching_listener(local_address, listeners)
|
|
217
|
-
listeners[listener_address].
|
|
224
|
+
listeners[listener_address].active_count += 1
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Count CLOSE_WAIT connections for each listener.
|
|
229
|
+
# These are accepted connections where the peer has closed its end but the
|
|
230
|
+
# application has not yet closed its side (e.g. still in rack.response_finished
|
|
231
|
+
# callbacks, or processing a request whose upstream already disconnected).
|
|
232
|
+
close_wait_connections.each do |local_address|
|
|
233
|
+
if listener_address = find_matching_listener(local_address, listeners)
|
|
234
|
+
listeners[listener_address].close_wait_count += 1
|
|
218
235
|
end
|
|
219
236
|
end
|
|
220
237
|
|
|
238
|
+
# /proc lists every ESTABLISHED child with the listener's local address, including
|
|
239
|
+
# sockets still in the accept queue. Those are already counted in queued_count on the
|
|
240
|
+
# LISTEN row (same meaning as Raindrops inet_diag queued vs inode != 0 for active).
|
|
241
|
+
listeners.each_value do |listener|
|
|
242
|
+
backlog = listener.queued_count
|
|
243
|
+
listener.active_count = [listener.active_count - backlog, 0].max
|
|
244
|
+
end
|
|
245
|
+
|
|
221
246
|
return listeners
|
|
222
247
|
rescue Errno::ENOENT, Errno::EACCES
|
|
223
248
|
return {}
|
|
@@ -254,13 +279,13 @@ class IO
|
|
|
254
279
|
|
|
255
280
|
state = state_hex.to_i(16)
|
|
256
281
|
|
|
257
|
-
listeners[path] ||= Listener.new(Addrinfo.unix(path), 0, 0)
|
|
282
|
+
listeners[path] ||= Listener.new(Addrinfo.unix(path), 0, 0, 0)
|
|
258
283
|
|
|
259
284
|
case state
|
|
260
285
|
when SS_CONNECTING # Queued connections
|
|
261
|
-
listeners[path].
|
|
286
|
+
listeners[path].queued_count += 1
|
|
262
287
|
when SS_CONNECTED # Active connections
|
|
263
|
-
listeners[path].
|
|
288
|
+
listeners[path].active_count += 1
|
|
264
289
|
end
|
|
265
290
|
end
|
|
266
291
|
|
data/lib/io/metrics/listener.rb
CHANGED
|
@@ -9,15 +9,17 @@ class IO
|
|
|
9
9
|
module Metrics
|
|
10
10
|
# Represents a network listener socket with its queue statistics.
|
|
11
11
|
# @attribute address [Addrinfo | Nil] Listening endpoint from capture; nil only for {Listener.zero} placeholders.
|
|
12
|
-
# @attribute
|
|
13
|
-
# @attribute
|
|
14
|
-
|
|
12
|
+
# @attribute queued_count [Integer] Number of connections waiting to be accepted (currently in the accept queue).
|
|
13
|
+
# @attribute active_count [Integer] Number of accepted connections in ESTABLISHED state.
|
|
14
|
+
# @attribute close_wait_count [Integer] Number of accepted connections in CLOSE_WAIT state (peer has closed; application still processing).
|
|
15
|
+
class Listener < Struct.new(:address, :queued_count, :active_count, :close_wait_count)
|
|
15
16
|
# Serialize for JSON; address uses Addrinfo#inspect_sockaddr.
|
|
16
17
|
def as_json(*)
|
|
17
18
|
{
|
|
18
19
|
address: address&.inspect_sockaddr,
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
queued_count: queued_count,
|
|
21
|
+
active_count: active_count,
|
|
22
|
+
close_wait_count: close_wait_count,
|
|
21
23
|
}
|
|
22
24
|
end
|
|
23
25
|
|
|
@@ -29,7 +31,7 @@ class IO
|
|
|
29
31
|
# Create a zero-initialized Listener instance (no endpoint; for tests or templates).
|
|
30
32
|
# @returns [Listener] Counters zero; {#address} is nil.
|
|
31
33
|
def self.zero
|
|
32
|
-
new(nil, 0, 0)
|
|
34
|
+
new(nil, 0, 0, 0) # address, queued_count, active_count, close_wait_count
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
# Whether listener stats can be captured on this system.
|
data/lib/io/metrics/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -14,6 +14,16 @@ Please see the [project documentation](https://socketry.github.io/io-metrics/) f
|
|
|
14
14
|
|
|
15
15
|
Please see the [project releases](https://socketry.github.io/io-metrics/releases/index) for all releases.
|
|
16
16
|
|
|
17
|
+
### v0.3.0
|
|
18
|
+
|
|
19
|
+
- **Breaking** Rename `Listener` fields: `queue_size` → `queued_count`, `active_connections` → `active_count`.
|
|
20
|
+
- Introduce `Listener#close_wait_count`: number of accepted connections in `CLOSE_WAIT` state (peer has closed; application still processing).
|
|
21
|
+
|
|
22
|
+
### v0.2.1
|
|
23
|
+
|
|
24
|
+
- Fixed `queue_size` under-reporting when multiple `SO_REUSEPORT` sockets share the same address — queue depths are now accumulated across all sockets rather than overwritten by the last one.
|
|
25
|
+
- **Linux** `Listener#active_connections` for TCP no longer counts sockets that are still in the kernel accept queue (those remain in `queue_size`). Counts now match the usual “past `accept()`” meaning and align with tools such as Raindrops’ `ListenStats#active`.
|
|
26
|
+
|
|
17
27
|
### v0.2.0
|
|
18
28
|
|
|
19
29
|
- **Breaking** `IO::Metrics::Listener.capture` returns an `Array` of `Listener` rows instead of a `Hash` keyed by address string.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.3.0
|
|
4
|
+
|
|
5
|
+
- **Breaking** Rename `Listener` fields: `queue_size` → `queued_count`, `active_connections` → `active_count`.
|
|
6
|
+
- Introduce `Listener#close_wait_count`: number of accepted connections in `CLOSE_WAIT` state (peer has closed; application still processing).
|
|
7
|
+
|
|
8
|
+
## v0.2.1
|
|
9
|
+
|
|
10
|
+
- Fixed `queue_size` under-reporting when multiple `SO_REUSEPORT` sockets share the same address — queue depths are now accumulated across all sockets rather than overwritten by the last one.
|
|
11
|
+
- **Linux** `Listener#active_connections` for TCP no longer counts sockets that are still in the kernel accept queue (those remain in `queue_size`). Counts now match the usual “past `accept()`” meaning and align with tools such as Raindrops’ `ListenStats#active`.
|
|
12
|
+
|
|
3
13
|
## v0.2.0
|
|
4
14
|
|
|
5
15
|
- **Breaking** `IO::Metrics::Listener.capture` returns an `Array` of `Listener` rows instead of a `Hash` keyed by address string.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
|
Binary file
|