@eik/core 2.1.74 → 2.1.75
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 +7 -0
- package/lib/multipart/parser.js +45 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [2.1.75](https://github.com/eik-lib/core/compare/v2.1.74...v2.1.75) (2026-08-14)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* cap concurrent sink write streams in _handleFile to 16 ([#634](https://github.com/eik-lib/core/issues/634)) ([c16c463](https://github.com/eik-lib/core/commit/c16c463f567b43fc617886e7f5d52acc1fd3d075))
|
|
7
|
+
|
|
1
8
|
## [2.1.74](https://github.com/eik-lib/core/compare/v2.1.73...v2.1.74) (2026-08-14)
|
|
2
9
|
|
|
3
10
|
|
package/lib/multipart/parser.js
CHANGED
|
@@ -158,6 +158,37 @@ const MultipartParser = class MultipartParser {
|
|
|
158
158
|
/** @type {Record<string,any>|null} */
|
|
159
159
|
let capturedEikJson = null;
|
|
160
160
|
|
|
161
|
+
// Limit how many sink write streams are open simultaneously.
|
|
162
|
+
// Without a cap, a tar with thousands of files would open an equal
|
|
163
|
+
// number of concurrent GCS resumable uploads or filesystem write
|
|
164
|
+
// streams, exhausting the connection pool or file descriptor limit.
|
|
165
|
+
const MAX_CONCURRENT_WRITES = 16;
|
|
166
|
+
let runningWrites = 0;
|
|
167
|
+
/** @type {import("tar").ReadEntry[]} */
|
|
168
|
+
const pendingEntries = [];
|
|
169
|
+
|
|
170
|
+
const drainPending = () => {
|
|
171
|
+
while (
|
|
172
|
+
runningWrites < MAX_CONCURRENT_WRITES &&
|
|
173
|
+
pendingEntries.length > 0
|
|
174
|
+
) {
|
|
175
|
+
const entry = /** @type {import("tar").ReadEntry} */ (
|
|
176
|
+
pendingEntries.shift()
|
|
177
|
+
);
|
|
178
|
+
runningWrites++;
|
|
179
|
+
const persist = this._persistFile({
|
|
180
|
+
incoming,
|
|
181
|
+
entry,
|
|
182
|
+
signal: ac.signal,
|
|
183
|
+
}).finally(() => {
|
|
184
|
+
runningWrites--;
|
|
185
|
+
drainPending();
|
|
186
|
+
});
|
|
187
|
+
persist.catch(() => {});
|
|
188
|
+
queue.push(persist);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
161
192
|
file.once("limit", () => {
|
|
162
193
|
this._log.warn(
|
|
163
194
|
`multipart - Uploaded package exceeds the max allowed file size - Field: ${fieldname} - Filename: ${filename} - Name: ${incoming.name} - Version: ${incoming.version} - Type: ${incoming.type} - Limit: ${this._pkgMaxFileSize} bytes`,
|
|
@@ -199,15 +230,13 @@ const MultipartParser = class MultipartParser {
|
|
|
199
230
|
return;
|
|
200
231
|
}
|
|
201
232
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
persist.catch(() => {});
|
|
210
|
-
queue.push(persist);
|
|
233
|
+
// Queue the entry. drainPending() starts it immediately if
|
|
234
|
+
// a slot is free, otherwise holds it until one opens up.
|
|
235
|
+
// Holding an entry without piping it blocks the tar parser
|
|
236
|
+
// at that position in the archive, creating natural
|
|
237
|
+
// backpressure on the upstream upload stream.
|
|
238
|
+
pendingEntries.push(entry);
|
|
239
|
+
drainPending();
|
|
211
240
|
},
|
|
212
241
|
});
|
|
213
242
|
|
|
@@ -220,6 +249,13 @@ const MultipartParser = class MultipartParser {
|
|
|
220
249
|
|
|
221
250
|
ac.abort();
|
|
222
251
|
|
|
252
|
+
// Drain any entries still waiting in the queue so the tar
|
|
253
|
+
// parser can wind down and the pipeline callback can fire.
|
|
254
|
+
for (const pending of pendingEntries) {
|
|
255
|
+
pending.resume();
|
|
256
|
+
}
|
|
257
|
+
pendingEntries.length = 0;
|
|
258
|
+
|
|
223
259
|
switch (error.code) {
|
|
224
260
|
case "TAR_BAD_ARCHIVE":
|
|
225
261
|
case "TAR_ABORT":
|