Skip to content

Commit b2e5852

Browse files
author
Leszek Zalewski
authored
Merge pull request #11 from lessonnine/TNT-2741/measure-socket-backlog
Pull info on socket backlog
2 parents 9f84831 + 434167f commit b2e5852

File tree

6 files changed

+104
-6
lines changed

6 files changed

+104
-6
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [1.1.0 Alpha]
11+
12+
### Added
13+
14+
Socket telemetry, and to be more precise new metric: `sockets.backlog`. If enabled it will
15+
pull information from Puma sockets about the state of their backlogs (requests waiting to
16+
be acknowledged by Puma). It will be exposed under `sockets-backlog` metric.
17+
18+
You can enable and test it via `config.sockets_telemetry!` option.
19+
1020
## [1.0.0] - 2021-09-08
1121
### Added
1222
- Release to Github Packages

Gemfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
PATH
22
remote: .
33
specs:
4-
puma-plugin-telemetry (0.3.1)
4+
puma-plugin-telemetry (1.1.0.alpha)
55
puma (>= 5.0)
66

77
GEM
@@ -14,7 +14,7 @@ GEM
1414
parallel (1.20.1)
1515
parser (3.0.2.0)
1616
ast (~> 2.4.1)
17-
puma (5.4.0)
17+
puma (5.5.2)
1818
nio4r (~> 2.0)
1919
rack (2.2.3)
2020
rainbow (3.0.0)

lib/puma/plugin/telemetry.rb

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ def configure
3030
yield(config)
3131
end
3232

33-
def build
34-
puma_telemetry
33+
def build(launcher = nil)
34+
socket_telemetry(puma_telemetry, launcher)
3535
end
3636

3737
private
@@ -47,6 +47,16 @@ def puma_telemetry
4747
.new(stats)
4848
.metrics(config.puma_telemetry)
4949
end
50+
51+
def socket_telemetry(telemetry, launcher)
52+
return telemetry if launcher.nil?
53+
return telemetry unless config.socket_telemetry?
54+
55+
telemetry.merge! SocketData.new(launcher.binder.ios)
56+
.metrics
57+
58+
telemetry
59+
end
5060
end
5161

5262
# Contents of actual Puma Plugin
@@ -71,7 +81,7 @@ def run!
7181
loop do
7282
@launcher.events.debug "plugin=telemetry msg=\"publish\""
7383

74-
call(Puma::Plugin::Telemetry.build)
84+
call(Puma::Plugin::Telemetry.build(@launcher))
7585
rescue Errno::EPIPE
7686
# Occurs when trying to output to STDOUT while puma is shutting down
7787
rescue StandardError => e

lib/puma/plugin/telemetry/config.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,31 @@ class Config
5858
# - default: DEFAULT_PUMA_TELEMETRY
5959
attr_accessor :puma_telemetry
6060

61+
# Whenever to publish socket telemetry.
62+
# - default: false
63+
attr_accessor :socket_telemetry
64+
6165
def initialize
6266
@enabled = false
6367
@initial_delay = 5
6468
@frequency = 5
6569
@targets = []
6670
@puma_telemetry = DEFAULT_PUMA_TELEMETRY
71+
@socket_telemetry = false
6772
end
6873

6974
def enabled?
7075
!!@enabled
7176
end
7277

78+
def socket_telemetry!
79+
@socket_telemetry = true
80+
end
81+
82+
def socket_telemetry?
83+
@socket_telemetry
84+
end
85+
7386
def add_target(name_or_target, **args)
7487
return @targets.push(name_or_target) unless name_or_target.is_a?(Symbol)
7588

lib/puma/plugin/telemetry/data.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,71 @@ def sum_stat(stat)
9595
end
9696
end
9797
end
98+
99+
# Pulls TCP INFO data from socket
100+
class SocketData
101+
UNACKED_REGEXP = /\ unacked=(?<unacked>\d+)\ /.freeze
102+
103+
def initialize(ios)
104+
@sockets = ios.select { |io| io.respond_to?(:getsockopt) }
105+
end
106+
107+
# Number of unacknowledged connections in the sockets, which
108+
# we know as socket backlog.
109+
#
110+
# The Socket::Option returned by `getsockopt` doesn't provide
111+
# any kind of accessors for data inside. It decodes it on demand
112+
# for `inspect` as strings in C implementation. It looks like
113+
#
114+
# #<Socket::Option: INET TCP INFO state=LISTEN
115+
# ca_state=Open
116+
# retransmits=0
117+
# probes=0
118+
# backoff=0
119+
# options=0
120+
# rto=0.000000s
121+
# ato=0.000000s
122+
# snd_mss=0
123+
# rcv_mss=0
124+
# unacked=0
125+
# sacked=5
126+
# lost=0
127+
# retrans=0
128+
# fackets=0
129+
# last_data_sent=0.000s
130+
# last_ack_sent=0.000s
131+
# last_data_recv=0.000s
132+
# last_ack_recv=0.000s
133+
# pmtu=0
134+
# rcv_ssthresh=0
135+
# rtt=0.000000s
136+
# rttvar=0.000000s
137+
# snd_ssthresh=0
138+
# snd_cwnd=10
139+
# advmss=0
140+
# reordering=3
141+
# rcv_rtt=0.000000s
142+
# rcv_space=0
143+
# total_retrans=0
144+
# (128 bytes too long)>
145+
#
146+
# That's why we have to pull the `unacked` field by parsing
147+
# `inspect` output, instead of using something like `opt.unacked`
148+
def unacked
149+
@sockets.sum do |socket|
150+
tcp_info = socket.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO).inspect
151+
tcp_match = tcp_info.match(UNACKED_REGEXP)
152+
153+
tcp_match[:unacked].to_i
154+
end
155+
end
156+
157+
def metrics
158+
{
159+
"sockets.backlog" => unacked
160+
}
161+
end
162+
end
98163
end
99164
end
100165
end

lib/puma/plugin/telemetry/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module Puma
44
class Plugin
55
module Telemetry
6-
VERSION = "1.0.0"
6+
VERSION = "1.1.0.alpha"
77
end
88
end
99
end

0 commit comments

Comments
 (0)