Skip to content

Conversation

@IlyasShabi
Copy link
Contributor

Description

This PR introduces process.locksCounters() to provide real time visibility into Web Locks API usage and contention metrics across the Node.js process.

Motivation

Lock contention can be difficult to diagnose in concurrent applications. Other platforms already make this easier,.NET has lock performance counters, Java has JMX for checking thread locks, and Go has mutex profiling.

Node.js added the Web Locks API in v24, but there’s currently no built-in way to see how locks are behaving. The new process.locksCounters() provide metrics to help developers monitor and debug lock related performance problems.

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/startup

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Oct 30, 2025
@IlyasShabi IlyasShabi marked this pull request as ready for review October 30, 2025 23:21
@codecov
Copy link

codecov bot commented Oct 31, 2025

Codecov Report

❌ Patch coverage is 97.64706% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.57%. Comparing base (bdf03bf) to head (68a2665).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/node_locks.cc 94.28% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #60502      +/-   ##
==========================================
+ Coverage   88.05%   88.57%   +0.51%     
==========================================
  Files         704      704              
  Lines      207857   207941      +84     
  Branches    39964    40054      +90     
==========================================
+ Hits       183030   184182    +1152     
+ Misses      16806    15803    -1003     
+ Partials     8021     7956      -65     
Files with missing lines Coverage Δ
lib/internal/bootstrap/node.js 99.58% <100.00%> (+<0.01%) ⬆️
lib/internal/process/per_thread.js 99.47% <100.00%> (+2.36%) ⬆️
src/node_locks.h 91.48% <100.00%> (+3.61%) ⬆️
src/node_process_methods.cc 89.07% <100.00%> (+0.38%) ⬆️
src/node_locks.cc 74.39% <94.28%> (+1.92%) ⬆️

... and 102 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nodejs nodejs deleted a comment from saico-0ps Oct 31, 2025
@IlyasShabi IlyasShabi requested review from Qard and addaleax October 31, 2025 22:03
Copy link
Member

@Qard Qard left a comment

Choose a reason for hiding this comment

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

I think we generally avoid adding more stuff to the process namespace. Probably better as another export in node:worker_threads given that's where the locks themselves are. 🤔

@IlyasShabi
Copy link
Contributor Author

@Qard I put it on process because Locks are shared across the entire process and It follows the same pattern as memoryUsage() , cpuUsage() , and resourceUsage().
I can also move it to worker_threads if there are concerns about growing the process namespace.

@joyeecheung
Copy link
Member

joyeecheung commented Nov 2, 2025

I think if we want to add a usage counter to process, it should ideally be generic, e.g. something like process.usageCounter('lock.abort'). We already have something similar in node_debug.h, just that so far these are limited to debug-only counters for internal tests.

Also, it would be nicer if the usage counter can be cleared/started/stopped at some given time instead of being recorded throughout the lifetime of the application.

@IlyasShabi
Copy link
Contributor Author

@joyeecheung I looked at the debug counters pattern, but I think it's better suited for internal testing rather than public API. I'm more with moving this to worker_threads as getLocksCounters() (happy to hear preferences on naming) and keep process namespace cleaner.

Also, it would be nicer if the usage counter can be cleared/started/stopped at some given time instead of being recorded throughout the lifetime of the application.

IMO I don't think they're a good fit for lock counters. These are lifetime counters, and adding clear function could creates problems: e.g. if library A clears the counters, library B loses visibility and can't get correct metrics.

Also, start/stop toggles make sense for heavy profiling (with stack traces), but for simple counter increments, the performance impact is negligible and could causes conflicts between libraries/workers

Comment on lines +842 to +843
Use [`process.locksCounters()`][] to monitor lock acquisitions, contention, and
queue sizes across the process.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be either all singular or all plural no ?

Suggested change
Use [`process.locksCounters()`][] to monitor lock acquisitions, contention, and
queue sizes across the process.
Use [`process.locksCounters()`][] to monitor lock acquisitions, contentions, and
queue sizes across the process.

threadCpuUsage: _threadCpuUsage,
memoryUsage: _memoryUsage,
rss,
locksCounters: _locksCounters,
Copy link
Contributor

Choose a reason for hiding this comment

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

We have cpuUsage, threadCpuUsage, memoryUsage, resourceUsage... maybe we should call it lockUsage instead ?

@IlyasShabi IlyasShabi requested a review from Qard November 18, 2025 10:49
@IlyasShabi
Copy link
Contributor Author

@Qard @joyeecheung I would like to continue on this. Any thought how we can make a decision?

@joyeecheung
Copy link
Member

IMO I don't think they're a good fit for lock counters. These are lifetime counters, and adding clear function could creates problems: e.g. if library A clears the counters, library B loses visibility and can't get correct metrics.

I meant that it would be nice to have individual counters that can be independently started/paused/stopped, so that e.g. you can use it in tests, as opposed to only having one global counter. For example, if one library A does diagnostics and another library B uses locks in some way, A can start its own counter to monitor code that depends on B, B won't be able to clear that counter unless it gets the handle of it somehow.

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

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants