@@ -4,115 +4,112 @@ import {
44 httpPoolFreeSockets ,
55 httpPoolPendingRequests ,
66} from '@internal/monitoring/metrics'
7- import Agent , { HttpsAgent } from 'agentkeepalive '
7+ import { Agent } from 'undici '
88import { getConfig } from '../../config'
99
1010const { region } = getConfig ( )
1111
1212export interface InstrumentedAgent {
13- httpAgent : Agent
14- httpsAgent : HttpsAgent
13+ dispatcher : Agent
1514 monitor : ( ) => NodeJS . Timeout | undefined
16- close : ( ) => void
15+ close : ( ) => Promise < void >
1716}
1817
1918export interface AgentStats {
2019 busySocketCount : number
2120 freeSocketCount : number
2221 pendingRequestCount : number
2322 errorSocketCount : number
24- timeoutSocketCount : number
25- createSocketErrorCount : number
23+ }
24+
25+ export interface AgentOptions {
26+ maxSockets : number
27+ connectTimeoutMs ?: number
28+ requestTimeoutMs ?: number
2629}
2730
2831/**
29- * Creates an instrumented agent
30- * Adding metrics to the agent
32+ * Creates an instrumented undici Agent.
33+ * Tracks connect errors via the Agent's `connectionError` event so the
34+ * `http_pool_errors` metric stays populated.
3135 */
32- export function createAgent ( name : string , options : { maxSockets : number } ) : InstrumentedAgent {
33- const agentOptions = {
34- maxSockets : options . maxSockets ,
35- keepAlive : true ,
36- keepAliveMsecs : 1000 ,
37- freeSocketTimeout : 1000 * 15 ,
38- }
36+ export function createAgent ( name : string , options : AgentOptions ) : InstrumentedAgent {
37+ const dispatcher = new Agent ( {
38+ connections : options . maxSockets ,
39+ keepAliveTimeout : 15_000 ,
40+ pipelining : 1 ,
41+ headersTimeout : options . requestTimeoutMs ?? 0 ,
42+ bodyTimeout : options . requestTimeoutMs ?? 0 ,
43+ connect : {
44+ timeout : options . connectTimeoutMs ?? 5_000 ,
45+ keepAlive : true ,
46+ keepAliveInitialDelay : 1_000 ,
47+ } ,
48+ } )
3949
40- const httpAgent = new Agent ( agentOptions )
41- const httpsAgent = new HttpsAgent ( agentOptions )
42- let watcher : NodeJS . Timeout | undefined = undefined
50+ let errorCount = 0
51+ dispatcher . on ( 'connectionError' , ( ) => {
52+ errorCount ++
53+ } )
54+
55+ let watcher : NodeJS . Timeout | undefined
56+ let closing : Promise < void > | undefined
4357
4458 return {
45- httpAgent,
46- httpsAgent,
59+ dispatcher,
4760 monitor : ( ) => {
48- const agent = watchAgent ( name , 'https' , httpsAgent )
49- watcher = agent
50- return agent
61+ watcher = watchAgent ( name , dispatcher , ( ) => errorCount )
62+ return watcher
5163 } ,
5264 close : ( ) => {
65+ if ( closing ) return closing
5366 if ( watcher ) {
5467 clearInterval ( watcher )
68+ watcher = undefined
5569 }
70+ closing = dispatcher . close ( )
71+ return closing
5672 } ,
5773 }
5874}
5975
60- /**
61- * Updates HTTP agent metrics
62- */
63- function updateHttpAgentMetrics ( name : string , protocol : string , stats : AgentStats ) {
64- const baseAttrs = { name, protocol }
76+ function updateHttpAgentMetrics ( name : string , stats : AgentStats ) {
77+ const baseAttrs = { name, protocol : 'https' }
6578
6679 httpPoolBusySockets . record ( stats . busySocketCount , baseAttrs )
6780 httpPoolFreeSockets . record ( stats . freeSocketCount , baseAttrs )
6881 httpPoolPendingRequests . record ( stats . pendingRequestCount , { name, region } )
69- httpPoolErrors . record ( stats . errorSocketCount , { ...baseAttrs , type : 'socket_error' } )
70- httpPoolErrors . record ( stats . timeoutSocketCount , { ...baseAttrs , type : 'timeout_socket_error' } )
71- httpPoolErrors . record ( stats . createSocketErrorCount , { ...baseAttrs , type : 'create_socket_error' } )
82+ httpPoolErrors . record ( stats . errorSocketCount , { ...baseAttrs , type : 'connect_error' } )
7283}
7384
74- export function watchAgent ( name : string , protocol : 'http' | 'https' , agent : Agent | HttpsAgent ) {
85+ export function watchAgent ( name : string , dispatcher : Agent , getErrorCount : ( ) => number ) {
7586 return setInterval ( ( ) => {
76- const httpStatus = agent . getCurrentStatus ( )
77-
78- const httpStats = gatherHttpAgentStats ( httpStatus )
79-
80- updateHttpAgentMetrics ( name , protocol , httpStats )
87+ const stats = gatherDispatcherStats ( dispatcher , getErrorCount ( ) )
88+ updateHttpAgentMetrics ( name , stats )
8189 } , 5000 )
8290}
8391
84- // Function to update metrics based on the current status of the agent
85- export function gatherHttpAgentStats ( status : Agent . AgentStatus ) {
86- // Calculate the number of busy sockets by iterating over the `sockets` object
92+ export function gatherDispatcherStats ( dispatcher : Agent , errorCount : number ) : AgentStats {
8793 let busySocketCount = 0
88- for ( const host in status . sockets ) {
89- if ( status . sockets . hasOwnProperty ( host ) ) {
90- busySocketCount += status . sockets [ host ]
91- }
92- }
93-
94- // Calculate the number of free sockets by iterating over the `freeSockets` object
9594 let freeSocketCount = 0
96- for ( const host in status . freeSockets ) {
97- if ( status . freeSockets . hasOwnProperty ( host ) ) {
98- freeSocketCount += status . freeSockets [ host ]
99- }
100- }
101-
102- // Calculate the number of pending requests by iterating over the `requests` object
10395 let pendingRequestCount = 0
104- for ( const host in status . requests ) {
105- if ( status . requests . hasOwnProperty ( host ) ) {
106- pendingRequestCount += status . requests [ host ]
96+
97+ for ( const origin of Object . keys ( dispatcher . stats ) ) {
98+ const s = dispatcher . stats [ origin ] as {
99+ running : number
100+ free ?: number
101+ pending : number
102+ queued ?: number
107103 }
104+ busySocketCount += s . running
105+ freeSocketCount += s . free ?? 0
106+ pendingRequestCount += s . pending + ( s . queued ?? 0 )
108107 }
109108
110109 return {
111110 busySocketCount,
112111 freeSocketCount,
113112 pendingRequestCount,
114- errorSocketCount : status . errorSocketCount ,
115- timeoutSocketCount : status . timeoutSocketCount ,
116- createSocketErrorCount : status . createSocketErrorCount ,
113+ errorSocketCount : errorCount ,
117114 }
118115}
0 commit comments