@@ -5,8 +5,9 @@ The following are metrics that Quilkin provides while in Proxy Mode.
55# ASN Maxmind Information
66
77If Quilkin is provided a a MaxmindDB GeoIP database, Quilkin will log the
8- following information in the ` maxmind information ` log, as well as populate
9- the following fields in any metrics with matching labels.
8+ following information in the ` maxmind information ` log. Only ` country_code ` , on
9+ ` quilkin_session_active ` , and ` asn ` , on the connection quality metrics below, are
10+ exported as labels; the rest are too high in cardinality to be.
1011
1112| Field | Description |
1213| -----------------| -----------------------------------------------|
@@ -31,12 +32,24 @@ The proxy exposes the following general metrics:
3132 * ` read ` : when the proxy receives data from a downstream connection on the listening port.
3233 * ` write ` : when the proxy sends data to a downstream connection via the listening port.
3334
34- * ` quilkin_packets_dropped_total{reason, asn, ip_prefix} ` (Counter)
35-
36- The total number of packets (not associated with any session) that were dropped by proxy.
37- Not that packets reflected by this metric were dropped at an earlier stage before they were associated with any session. For session based metrics, see the list of [ session metrics] [ session-metrics ] instead.
38- * ` reason = NoConfiguredEndpoints `
39- * ` NoConfiguredEndpoints ` : No upstream endpoints were available to send the packet to. This can occur e.g if the endpoints cluster was scaled down to zero and the proxy is configured via a control plane.
35+ * ` quilkin_packets_dropped_total{event, reason, filter, destination} ` (Counter)
36+
37+ The total number of packets that were dropped by the proxy.
38+ * The ` reason ` label is a closed set, so a breakdown built on it survives a
39+ filter being renamed or an ` errno ` producing different text:
40+ * ` no_endpoint_match ` : no upstream endpoint was available, or none matched the packet's routing token.
41+ * ` filter_drop ` : a filter chose to drop the packet, ie the chain worked as configured.
42+ * ` filter_error ` : a filter failed to process the packet.
43+ * ` socket_error ` : the socket refused the packet, or the packet couldn't be built for it.
44+ * ` queue_full ` : a send or receive queue was full.
45+ * ` invalid_packet ` : the packet couldn't be parsed as a datagram Quilkin handles. The specific parse failure is logged rather than labelled.
46+ * ` session_limit ` : the session limit was reached, so no session could be established.
47+ * ` internal ` : Quilkin lost track of state it needed to forward the packet.
48+ * The ` filter ` label is the filter responsible, and is empty when the drop
49+ wasn't a filter's decision.
50+ * The ` destination ` label is described under
51+ [ ` quilkin_bytes_total ` ] ( #general-metrics ) , and is empty for packets dropped
52+ before they were routed.
4053
4154* ` quilkin_cluster_active `
4255
@@ -47,27 +60,46 @@ The proxy exposes the following general metrics:
4760 The number of currently active upstream endpoints. Note that this tracks the number of endpoints that the proxy
4861 knows of rather than those that it is connected to (see [ Session Metrics] [ session-metrics ] instead for those)
4962
50- * ` quilkin_bytes_total{event, asn, ip_prefix } `
63+ * ` quilkin_bytes_total{event, destination } `
5164
5265 The total number of bytes sent or received
5366 * The ` event ` label is either:
5467 * ` read ` : when the proxy receives data from a downstream connection on the listening port.
5568 * ` write ` : when the proxy sends data to a downstream connection via the listening port.
69+ * The ` destination ` label is the cluster the packet was routed to, so traffic
70+ can be attributed to a gameserver fleet rather than only counted in aggregate.
71+ It carries the same values as ` quilkin_active_endpoints ` , so the two can be
72+ joined.
73+
74+ The cluster comes from the routing decision itself, not from looking the
75+ address up afterwards, so it is the cluster the packet actually went through
76+ even when the same endpoint is configured in more than one. It is empty when
77+ the destination didn't come from a cluster with a locality, which includes
78+ destinations a filter decoded from the packet rather than selecting from the
79+ cluster map. Note that ` event=write ` traffic carries the cluster of the
80+ gameserver that sent it, not of the client it is going to.
5681
57- * ` quilkin_packets_total{event, asn, ip_prefix } `
82+ * ` quilkin_packets_total{event, destination } `
5883
5984 The total number of packets sent or recieved.
60- * The ` event ` label is either:
61- * ` read ` : when the proxy receives data from a downstream connection on the listening port.
62- * ` write ` : when the proxy sends data to a downstream connection via the listening port.
85+ * The labels are the same as [ ` quilkin_bytes_total ` ] ( #general-metrics ) .
86+
87+ * ` quilkin_packet_jitter{event} `
6388
64- * ` quilkin_packet_jitter{event, asn, ip_prefix} `
89+ The time between packets arriving at an I/O loop (in nanoseconds). This covers
90+ every session the loop serves, so it is a whole-proxy figure; for the
91+ distribution across players use ` quilkin_session_jitter_seconds ` .
6592
66- The time between receiving new packets (in nanoseconds).
93+ The series stops being exported when no packet arrived during an aggregation
94+ interval, rather than continuing to publish the last value the proxy saw.
6795
68- * ` quilkin_errors_total{event, asn, ip_prefix } `
96+ * ` quilkin_errors_total{event, reason } `
6997
7098 The total number of errors encountered while reading a packet from the upstream endpoint.
99+ * The ` reason ` label is a closed set, replacing the previous free-text ` display `
100+ label. For I/O errors it names the ` errno ` , eg ` invalid_input ` ,
101+ ` no_buffer_space ` or ` message_too_long ` , rather than interpolating the
102+ platform's error string.
71103
72104* ` quilkin_game_traffic_tasks `
73105
@@ -97,6 +129,88 @@ The proxy exposes the following metrics around sessions:
97129
98130 The total number of sessions that have been created.
99131
132+ * ` quilkin_sessions_closed_total{reason} ` (Counter)
133+
134+ The total number of sessions that have ended, by why they ended. A fall in
135+ session count otherwise looks the same whether players left or their endpoints
136+ vanished.
137+ * ` idle_timeout ` : no traffic within the session TTL. UDP has no close, so this
138+ is what a player leaving normally looks like.
139+ * ` endpoint_gone ` : the endpoint the session was routed to is no longer in the
140+ cluster map. Only reported for destinations that were in the cluster map when
141+ the session was created, so an endpoint configured by name is never
142+ misattributed here.
143+ * ` shutdown ` : the proxy is shutting down.
144+
145+ ## Connection Quality Metrics
146+
147+ A player's jitter and their ISP are per-player facts, so neither can be a metric
148+ label: concurrent sessions and the thousands of ASNs seen in a day of traffic both
149+ blow up cardinality. Instead the proxy tracks quality per session internally and
150+ periodically exports a projection whose series count is bounded by configuration
151+ rather than by traffic. See the ` --service.udp.metrics.* ` options for the
152+ tunables.
153+
154+ Note the division of labour. Whether an individual * session* is having a bad time
155+ is a judgement the proxy makes, because it is the only thing holding that
156+ session's packet timing. Whether an * ISP* is having a bad time is left to the
157+ consumer of these metrics: a proxy carries on the order of a hundred concurrent
158+ sessions spread over thousands of ASNs, so no single one sees enough of any ASN to
159+ threshold on. The proxy exports the numerator and the denominator per ASN and
160+ expects them to be summed across the fleet before any conclusion is drawn.
161+
162+ Interarrival jitter is measured against a monotonic clock, and gaps longer than a
163+ second are treated as the stream restarting rather than as jitter, so a player
164+ pausing doesn't register as a player with a bad connection.
165+
166+ Sessions the proxy saw no traffic for during an interval are counted, but
167+ contribute no quality judgement.
168+
169+ * ` quilkin_session_jitter_seconds ` (Histogram)
170+
171+ The distribution of per-session interarrival jitter of downstream packets, as
172+ the RFC 3550 estimator. A histogram rather than a mean, because a mean of 0.5 ms
173+ is compatible with a few percent of players at 80 ms, and those are the players
174+ worth knowing about — ` histogram_quantile ` over this answers "what does the 99th
175+ percentile player at this proxy see".
176+
177+ Only jitter is exported per session. Loss and round-trip time on the client's
178+ leg need either packet sequence numbers or a client-side timestamp, and the
179+ proxy has neither.
180+
181+ * ` quilkin_sessions_active_by_asn{asn} ` (Gauge)
182+
183+ Active sessions by the client's ASN, for the largest
184+ ` --service.udp.metrics.top-asns ` ASNs at this proxy, and the denominator for the
185+ metric below.
186+
187+ Sessions belonging to any other ASN are counted under ` asn="other" ` , so the
188+ breakdown always sums to the session total. Sessions whose client IP resolved to
189+ no ASN are counted under ` asn="unknown" ` . Setting ` --top-asns ` to 0 disables
190+ per-ASN reporting entirely.
191+
192+ * ` quilkin_client_sessions_degraded{asn, reason} ` (Gauge)
193+
194+ The number of sessions breaching a quality threshold, by the client's ASN. Only
195+ ASNs currently breaching are exported, so a healthy proxy publishes nothing
196+ here and the series count follows the number of ISPs actually in trouble.
197+ * ` reason = jitter ` : sessions at or above ` --service.udp.metrics.jitter-threshold-ms ` .
198+
199+ This is a count, not a verdict. For the affected share of an ISP, divide by
200+ ` quilkin_sessions_active_by_asn ` — summing both across proxies first, since one
201+ proxy's view of a single ASN is too small a sample to threshold on:
202+
203+ ``` promql
204+ sum by (asn) (quilkin_client_sessions_degraded{reason="jitter"})
205+ / sum by (asn) (quilkin_sessions_active_by_asn)
206+ ```
207+
208+ * ` quilkin_client_sessions_degraded_total{reason} ` (Counter)
209+
210+ The total number of times a session was observed breaching a threshold, for
211+ alerting on a rate without depending on a share the proxy would have to pick a
212+ cut-off for.
213+
100214## Filter Metrics
101215Quilkin's filters use a set of generic metric keys, to make it easier to build visualisations that can account for
102216a dynamic set of filters that can be added, removed, or updated at runtime with different configurations. All of
0 commit comments