Skip to content

Conversation

@tdeekens
Copy link
Contributor

@tdeekens tdeekens commented Apr 12, 2025

This relates to:

This relates to the issue #4149

Rationale

Integrating existing and exposed Pool stats into a telemetry system is a bit cumbersome. Right now we can hook into the factory function and observe the created pool there. It would be beneficial if a Pool's and Client's stats would be exposed by the Agent owning the Pool or Client respectively.

Changes

Exposing stats through the Agent makes it easier to integrate telemetry systems as we do not have to "hook" into the factory to receive the Pool or Client used by an Agent.

Features

Exposes a new getter called stats on an Agent. The method returns an Array of Arrays with stats by origin.

Additionally, the Client now also exposes available stats similar to a Pool. The initial implementation of Pool stats happened in this PR while the implementation for a Client here follows the Pool's implementation pattenr.

Bug Fixes

Breaking Changes and Deprecations

Status

kHttpProxyAgent: Symbol('http proxy agent'),
kHttpsProxyAgent: Symbol('https proxy agent')
kHttpsProxyAgent: Symbol('https proxy agent'),
kStats: Symbol('stats')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used now by Pool and Client. Hence it should be shared in my opinion.

@@ -0,0 +1,28 @@
'use strict'
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follows implementation of PoolStats. Clients however expose a subset of stats. Hence the separate class.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll put this class in utils or so, as the files within the dispatcher/ dir are mostly mean for classes that shares dispatcher interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Same for the PoolStats then which prior to this change lived here too, right?

this[kResume] = (sync) => resume(this, sync)
this[kOnError] = (err) => onError(this, err)

this[kStats] = new ClientStats(this)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as with Pools.

@metcoder95 metcoder95 changed the title Add stats of client and pool to be expored through agent feat: Add stats of client and pool to be expored through agent Apr 13, 2025
Copy link
Member

@metcoder95 metcoder95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add testing for it?

@@ -0,0 +1,28 @@
'use strict'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll put this class in utils or so, as the files within the dispatcher/ dir are mostly mean for classes that shares dispatcher interface.

@tdeekens
Copy link
Contributor Author

Can we add testing for it?

Yip. I followed the Pool Stats addition PR in terms of depth of testing on only existing through tsd.

@tdeekens
Copy link
Contributor Author

tdeekens commented Apr 13, 2025

Hi @metcoder95!

In ab942e4 I've moved the pool stats both of pool and client into a utils/stats.js. Happy for feedback.
Furthermore in f2452c4 I added some tests. Let me know if the direction is right and if the depth needs to be expanded on.

Should we also add docs and maybe an example?

@tdeekens
Copy link
Contributor Author

👋🏼 @metcoder95 in 15bc63b I started adding some documentation where I thought it would help.

@tdeekens tdeekens requested a review from metcoder95 April 15, 2025 21:34
@tdeekens tdeekens changed the title feat: Add stats of client and pool to be expored through agent Add stats of client and pool to be expored through agent Apr 16, 2025
@tdeekens
Copy link
Contributor Author

Anything else you see should be improved @metcoder95? Thanks!

@tdeekens tdeekens requested a review from metcoder95 April 22, 2025 06:46
@tdeekens tdeekens marked this pull request as ready for review April 22, 2025 06:46
@tdeekens
Copy link
Contributor Author

@metcoder95 can you approve the workflows again. There was a type issue on the connected stat of a client which is a boolean compared to a pool where it is a number. Thanks!

@tdeekens
Copy link
Contributor Author

@metcoder95 I can't see that tests fail due to my changes with

Error: Cannot find module '../../deps/undici/src/index.js'
Require stack:
- /home/runner/work/undici/undici/node-v23.11.0/test/parallel/test-inspector-network-fetch.js
    at Function._resolveFilename (node:internal/modules/cjs/loader:1405:15)
    at defaultResolveImpl (node:internal/modules/cjs/loader:1061:19)
    at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1066:22)
    at Function._load (node:internal/modules/cjs/loader:1215:37)
    at TracingChannel.traceSync (node:diagnostics_channel:322:14)
    at wrapModuleLoad (node:internal/modules/cjs/loader:235:24)
    at Module.require (node:internal/modules/cjs/loader:1491:12)
    at require (node:internal/modules/helpers:135:16)
    at Object.<anonymous> (/home/runner/work/undici/undici/node-v23.11.0/test/parallel/test-inspector-network-fetch.js:15:16)
    at Module._compile (node:internal/modules/cjs/loader:1734:14) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/home/runner/work/undici/undici/node-v23.11.0/test/parallel/test-inspector-network-fetch.js'
  ]
}

or

✖ SqliteCacheStore works nicely with multiple stores (22.842ms)
✔ SqliteCacheStore maxEntries (4.6677ms)
✔ SqliteCacheStore two writes (0.8061ms)
✔ SqliteCacheStore write & read (1.3409ms)
Error: [Error [ERR_TEST_FAILURE]: failed running after hook] { code: 'ERR_TEST_FAILURE', failureType: 'hookFailed', cause: [Error: EBUSY: resource busy or locked, unlink 'D:\a\undici\undici\cache-interceptor.sqlite'] { errno: -4082, code: 'EBUSY', syscall: 'unlink', path: 'D:\\a\\undici\\undici\\cache-interceptor.sqlite' } }

is that something you can help with? I've little to no experience with the CI setup of this repository.

get size () {
return this[kPool][kSize]
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand this design. Instead of using getters, I would allocate new objects. This ensures that data is stable in time without having to copy them.

Copy link
Contributor Author

@tdeekens tdeekens Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be fair I also didn't but this it the current implementation and I am following it assuming it has reasoning. In that this code is just moved from elsewhere as requested by @metcoder95.

Do you wish to have this changed here or as a follow up?

Copy link
Contributor Author

@tdeekens tdeekens Apr 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👋🏼 What's the implicit agreement here? Refactor as a follow-up, not at all or here? I understand the issue that the using getters can cause inconsistent as a whole with single values. I assume we would return an object of a "stats bag" in the pool and agent. Just not sure when/where to implement it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@metcoder95 what's missing on this PR to land this? Anything expected from my side?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just applying the suggested refactor from getters to a plain object (it can also come from a class, but avoiding the getters/setters).

Copy link
Contributor Author

@tdeekens tdeekens Apr 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. From what I understand 7ac13ff is what you had in mind?

@tdeekens tdeekens requested a review from mcollina April 26, 2025 20:12
@tdeekens tdeekens changed the title Add stats of client and pool to be expored through agent Add stats of client and pool to be accessible through agent Apr 29, 2025
@tdeekens tdeekens force-pushed the add-agent-pool-client-stats branch from 55566bb to 7ac13ff Compare April 29, 2025 20:52
kNoProxyAgent: Symbol('no proxy agent'),
kHttpProxyAgent: Symbol('http proxy agent'),
kHttpsProxyAgent: Symbol('https proxy agent'),
kStats: Symbol('stats')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed anymore. Not assigned as property any longer.


get stats () {
return this[kStats]
return new ClientStats(this)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New class every time accessed.

this[kClient] = client
}

get connected () {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not getters just instance properties.

@tdeekens
Copy link
Contributor Author

@metcoder95 the failing tests appear to be unrelated.

✔ SqliteCacheStore (16.3615ms)
✖ SqliteCacheStore works nicely with multiple stores (19.9884ms)
✔ SqliteCacheStore maxEntries (4.9747ms)
✔ SqliteCacheStore two writes (0.8221ms)
✔ SqliteCacheStore write & read (1.2954ms)
Error: [Error [ERR_TEST_FAILURE]: failed running after hook] { code: 'ERR_TEST_FAILURE', failureType: 'hookFailed', cause: [Error: EBUSY: resource busy or locked, unlink 'D:\a\undici\undici\cache-interceptor.sqlite'] { errno: -4082, code: 'EBUSY', syscall: 'unlink', path: 'D:\\a\\undici\\undici\\cache-interceptor.sqlite' } }

Is this a known issue. Something I have to or you could have a look into?

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mcollina
Copy link
Member

PTAL @ronag @metcoder95

Copy link
Member

@metcoder95 metcoder95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@tdeekens
Copy link
Contributor Author

tdeekens commented May 2, 2025

@metcoder95 I need some help with the failing tests. I can't see them as relating to the proposed changes. Can you provide some guidance?

@metcoder95
Copy link
Member

Feel free them, they are not related to your changes as you suggests; I'm starting to dig deeper into it to see if can sort it out.

@tdeekens
Copy link
Contributor Author

tdeekens commented May 7, 2025

@metcoder95 sorry to nudge but did you end up anywhere? I tried to understand what's going on but can not find the root cause.

@metcoder95
Copy link
Member

They are not a blocker for your PR but rather due to other reasons; feel free to ignore them

@tdeekens
Copy link
Contributor Author

tdeekens commented May 8, 2025

They are not a blocker for your PR but rather due to other reasons; feel free to ignore them

@metcoder95 Does that mean you would merge the PR or is something else missing to land this?

@metcoder95
Copy link
Member

LGTM, waiting for @ronag review

@tdeekens
Copy link
Contributor Author

tdeekens commented May 8, 2025

Whooop. Thanks all. All approvals are there. I am excited. /cc @metcoder95

@metcoder95 metcoder95 merged commit c9f50c4 into nodejs:main May 9, 2025
28 of 31 checks passed
@github-actions github-actions bot mentioned this pull request May 10, 2025
@tdeekens
Copy link
Contributor Author

tdeekens commented May 11, 2025

Thanks for the support and release. The next version of @promster/undici will support exporting agent metrics to Prometheus.

tdeekens/promster#1385

@github-actions github-actions bot mentioned this pull request May 12, 2025
@Ethan-Arrowood
Copy link
Collaborator

Was it intentional to omit stats from Client directly? I didn't read through every thread here, but I've been loving pool.stats I expected it to exist on client as well but it seems only on agent? Happy to contribute this unless there was a reason it was omitted in the first place.

@Ethan-Arrowood
Copy link
Collaborator

Lol please ignore me. I didn't realize I was using undici v6 and this was added in v7. 🤦‍♂️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants