@instana/shared-metrics 4.26.3 → 4.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [4.27.0](https://github.com/instana/nodejs/compare/v4.26.4...v4.27.0) (2025-10-23)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **shared-metrics:** reduce memory consumption with many dependencies ([#2088](https://github.com/instana/nodejs/issues/2088)) ([7e0b029](https://github.com/instana/nodejs/commit/7e0b029331a6657c64b29e875a529d03952e7414))
12
+
13
+
14
+
15
+
16
+
17
+ ## [4.26.4](https://github.com/instana/nodejs/compare/v4.26.3...v4.26.4) (2025-10-21)
18
+
19
+ **Note:** Version bump only for package @instana/shared-metrics
20
+
21
+
22
+
23
+
24
+
6
25
  ## [4.26.3](https://github.com/instana/nodejs/compare/v4.26.2...v4.26.3) (2025-10-21)
7
26
 
8
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instana/shared-metrics",
3
- "version": "4.26.3",
3
+ "version": "4.27.0",
4
4
  "description": "Internal metrics plug-in package for Node.js monitoring with Instana",
5
5
  "author": {
6
6
  "name": "Bastian Krol",
@@ -59,7 +59,7 @@
59
59
  },
60
60
  "license": "MIT",
61
61
  "dependencies": {
62
- "@instana/core": "4.26.3",
62
+ "@instana/core": "4.27.0",
63
63
  "detect-libc": "^2.1.2",
64
64
  "event-loop-lag": "^1.4.0",
65
65
  "semver": "^7.7.3",
@@ -72,5 +72,5 @@
72
72
  "event-loop-stats": "1.4.1",
73
73
  "gcstats.js": "1.0.0"
74
74
  },
75
- "gitHead": "039459e615ce00815498d7a6f6c9669dfeff307a"
75
+ "gitHead": "dd557cc7ae7bd26a2399ffc821031969cdb8d5a1"
76
76
  }
@@ -27,6 +27,8 @@ exports.MAX_DEPENDENCIES = 750;
27
27
  /** @type {string} */
28
28
  exports.payloadPrefix = 'dependencies';
29
29
 
30
+ const MAX_DEPTH_NODE_MODULES = 2;
31
+
30
32
  /** @type {Object.<string, string>} */
31
33
  const preliminaryPayload = {};
32
34
 
@@ -87,7 +89,13 @@ exports.activate = function activate() {
87
89
  * @param {string} packageJsonPath
88
90
  */
89
91
  function addAllDependencies(dependencyDir, started, packageJsonPath) {
90
- addDependenciesFromDir(dependencyDir, () => {
92
+ addDependenciesFromDir(dependencyDir, 0, () => {
93
+ // TODO: This check happens AFTER we have already collected the dependencies.
94
+ // This is quiet useless for a large dependency tree, because we consume resources to collect
95
+ // all the dependencies (fs.stats, fs.readFile etc), but then discard most of them here.
96
+ // This is only critical for a very large number of defined dependencies in package.json (vertical).
97
+ // NOTE: There is an extra protection in the `addDependenciesFromDir` fn to
98
+ // limit the depth of traversing node_modules.
91
99
  if (Object.keys(preliminaryPayload).length <= exports.MAX_DEPENDENCIES) {
92
100
  // @ts-ignore: Cannot redeclare exported variable 'currentPayload'
93
101
  exports.currentPayload = preliminaryPayload;
@@ -114,7 +122,11 @@ function addAllDependencies(dependencyDir, started, packageJsonPath) {
114
122
  * @param {string} dependencyDir
115
123
  * @param {() => void} callback
116
124
  */
117
- function addDependenciesFromDir(dependencyDir, callback) {
125
+ function addDependenciesFromDir(dependencyDir, currentDepth = 0, callback) {
126
+ if (currentDepth >= MAX_DEPTH_NODE_MODULES) {
127
+ return callback();
128
+ }
129
+
118
130
  fs.readdir(dependencyDir, (readDirErr, dependencies) => {
119
131
  if (readDirErr || !dependencies) {
120
132
  logger.warn(`Cannot analyse dependencies due to ${readDirErr?.message}`);
@@ -140,11 +152,13 @@ function addDependenciesFromDir(dependencyDir, callback) {
140
152
 
141
153
  filteredDependendencies.forEach(dependency => {
142
154
  if (dependency.indexOf('@') === 0) {
143
- addDependenciesFromDir(path.join(dependencyDir, dependency), () => {
155
+ // NOTE: We do not increase currentDepth because scoped packages are just a folder containing more packages.
156
+ addDependenciesFromDir(path.join(dependencyDir, dependency), currentDepth, () => {
144
157
  countDownLatch.countDown();
145
158
  });
146
159
  } else {
147
160
  const fullDirPath = path.join(dependencyDir, dependency);
161
+
148
162
  // Only check directories. For example, yarn adds a .yarn-integrity file to /node_modules/ which we need to
149
163
  // exclude, otherwise we get a confusing "Failed to identify version of .yarn-integrity dependency due to:
150
164
  // ENOTDIR: not a directory, open '.../node_modules/.yarn-integrity/package.json'." in the logs.
@@ -159,7 +173,7 @@ function addDependenciesFromDir(dependencyDir, callback) {
159
173
  return;
160
174
  }
161
175
 
162
- addDependency(dependency, fullDirPath, countDownLatch);
176
+ addDependency(dependency, fullDirPath, countDownLatch, currentDepth);
163
177
  });
164
178
  }
165
179
  });
@@ -173,9 +187,11 @@ function addDependenciesFromDir(dependencyDir, callback) {
173
187
  * @param {string} dependency
174
188
  * @param {string} dependencyDirPath
175
189
  * @param {import('./util/CountDownLatch')} countDownLatch
190
+ * @param {number} currentDepth
176
191
  */
177
- function addDependency(dependency, dependencyDirPath, countDownLatch) {
192
+ function addDependency(dependency, dependencyDirPath, countDownLatch, currentDepth) {
178
193
  const packageJsonPath = path.join(dependencyDirPath, 'package.json');
194
+
179
195
  fs.readFile(packageJsonPath, { encoding: 'utf8' }, (err, contents) => {
180
196
  if (err && err.code === 'ENOENT') {
181
197
  // This directory does not contain a package json. This happens for example for node_modules/.cache etc.
@@ -198,19 +214,23 @@ function addDependency(dependency, dependencyDirPath, countDownLatch) {
198
214
  preliminaryPayload[parsedPackageJson.name] = parsedPackageJson.version;
199
215
  }
200
216
  } catch (parseErr) {
217
+ // TODO: countDownLatch.countDown(); needs to be called here too?
218
+ // countDownLatch.countDown();
201
219
  return logger.info(
202
220
  `Failed to identify version of ${dependency} dependency due to: ${parseErr?.message}.
203
221
  This means that you will not be able to see details about this dependency within Instana.`
204
222
  );
205
223
  }
206
224
 
225
+ // NOTE: The dependency metric collector does not respect if the node_modules are dev dependencies or production
226
+ // dependencies. It collects all dependencies that are installed in the node_modules folder.
207
227
  const potentialNestedNodeModulesFolder = path.join(dependencyDirPath, 'node_modules');
208
228
  fs.stat(potentialNestedNodeModulesFolder, (statErr, stats) => {
209
229
  if (statErr || !stats.isDirectory()) {
210
230
  countDownLatch.countDown();
211
231
  return;
212
232
  }
213
- addDependenciesFromDir(potentialNestedNodeModulesFolder, () => {
233
+ addDependenciesFromDir(potentialNestedNodeModulesFolder, currentDepth + 1, () => {
214
234
  countDownLatch.countDown();
215
235
  });
216
236
  });