@forgezero/agent 0.1.114 → 0.1.116
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/dist/agent-heartbeat.js +1 -1
- package/dist/bootstrap.js +42 -1
- package/dist/capacity-calibration.d.ts +4 -0
- package/dist/capacity-calibration.js +7 -4
- package/dist/definition.js +9 -4
- package/dist/deploy-compiler.js +75 -1
- package/dist/deploy-file.js +9 -4
- package/dist/deploy-plan-runner.js +75 -1
- package/dist/deploy-plan.js +75 -1
- package/dist/deploy.d.ts +14 -0
- package/dist/fz-agent.js +83 -10
- package/dist/fz.js +68 -12
- package/dist/metal-bootstrap.js +1 -1
- package/dist/operator-bootstrap.d.ts +1 -1
- package/dist/operator-bootstrap.js +46 -5
- package/dist/platform-bootstrap-runtime.js +3 -0
- package/dist/platform-fleet-verification.js +13 -5
- package/dist/platform-genesis.d.ts +2 -0
- package/dist/platform-genesis.js +4 -0
- package/dist/provision.js +10 -5
- package/dist/software-helper.js +9 -4
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1415,6 +1415,7 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
1415
1415
|
const endpoint = localCalibrationEndpoint(options.endpoint);
|
|
1416
1416
|
const maxConcurrency = options.maxConcurrency ?? 256;
|
|
1417
1417
|
const requestsPerWorker = options.requestsPerWorker ?? 8;
|
|
1418
|
+
const maximumRequestsPerWorker = options.maximumRequestsPerWorker ?? 100;
|
|
1418
1419
|
const maxP95Ms = options.maxP95Ms ?? 250;
|
|
1419
1420
|
const maxErrorRate = options.maxErrorRate ?? 0.01;
|
|
1420
1421
|
const safetyRatio = options.safetyRatio ?? 0.8;
|
|
@@ -1422,13 +1423,14 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
1422
1423
|
const minimumStageDurationMs = options.minimumStageDurationMs ?? 5000;
|
|
1423
1424
|
const maxCpuUtilizationPercent = options.maxCpuUtilizationPercent ?? 90;
|
|
1424
1425
|
const maxMemoryUtilizationPercent = options.maxMemoryUtilizationPercent ?? 90;
|
|
1425
|
-
if (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1 || maxConcurrency > 4096 || !Number.isSafeInteger(requestsPerWorker) || requestsPerWorker < 2 || requestsPerWorker > 100 || !Number.isFinite(maxP95Ms) || maxP95Ms < 1 || !Number.isFinite(maxErrorRate) || maxErrorRate < 0 || maxErrorRate > 0.2 || !Number.isFinite(safetyRatio) || safetyRatio < 0.25 || safetyRatio > 0.95 || !Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 100 || requestTimeoutMs > 30000 || !Number.isSafeInteger(minimumStageDurationMs) || minimumStageDurationMs < 100 || minimumStageDurationMs > 60000 || !Number.isFinite(maxCpuUtilizationPercent) || maxCpuUtilizationPercent < 25 || maxCpuUtilizationPercent > 100 || !Number.isFinite(maxMemoryUtilizationPercent) || maxMemoryUtilizationPercent < 25 || maxMemoryUtilizationPercent > 100) {
|
|
1426
|
+
if (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1 || maxConcurrency > 4096 || !Number.isSafeInteger(requestsPerWorker) || requestsPerWorker < 2 || requestsPerWorker > 100 || !Number.isSafeInteger(maximumRequestsPerWorker) || maximumRequestsPerWorker < requestsPerWorker || maximumRequestsPerWorker > 100 || !Number.isFinite(maxP95Ms) || maxP95Ms < 1 || !Number.isFinite(maxErrorRate) || maxErrorRate < 0 || maxErrorRate > 0.2 || !Number.isFinite(safetyRatio) || safetyRatio < 0.25 || safetyRatio > 0.95 || !Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 100 || requestTimeoutMs > 30000 || !Number.isSafeInteger(minimumStageDurationMs) || minimumStageDurationMs < 100 || minimumStageDurationMs > 60000 || !Number.isFinite(maxCpuUtilizationPercent) || maxCpuUtilizationPercent < 25 || maxCpuUtilizationPercent > 100 || !Number.isFinite(maxMemoryUtilizationPercent) || maxMemoryUtilizationPercent < 25 || maxMemoryUtilizationPercent > 100) {
|
|
1426
1427
|
throw new Error("Capacity calibration bounds are invalid.");
|
|
1427
1428
|
}
|
|
1428
1429
|
return {
|
|
1429
1430
|
endpoint,
|
|
1430
1431
|
maxConcurrency,
|
|
1431
1432
|
requestsPerWorker,
|
|
1433
|
+
maximumRequestsPerWorker,
|
|
1432
1434
|
maxP95Ms,
|
|
1433
1435
|
maxErrorRate,
|
|
1434
1436
|
safetyRatio,
|
|
@@ -1438,7 +1440,7 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
1438
1440
|
maxMemoryUtilizationPercent
|
|
1439
1441
|
};
|
|
1440
1442
|
}
|
|
1441
|
-
async function stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimumDurationMs, fetcher, sampler) {
|
|
1443
|
+
async function stage(endpoint, concurrency, requestsPerWorker, maximumRequestsPerWorker, timeoutMs, minimumDurationMs, fetcher, sampler) {
|
|
1442
1444
|
const latencies = [];
|
|
1443
1445
|
let succeeded = 0;
|
|
1444
1446
|
let failed = 0;
|
|
@@ -1447,7 +1449,7 @@ async function stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimu
|
|
|
1447
1449
|
const started = performance.now();
|
|
1448
1450
|
await Promise.all(Array.from({ length: concurrency }, async () => {
|
|
1449
1451
|
let request = 0;
|
|
1450
|
-
while (request < requestsPerWorker || performance.now() - started < minimumDurationMs) {
|
|
1452
|
+
while (request < maximumRequestsPerWorker && (request < requestsPerWorker || performance.now() - started < minimumDurationMs)) {
|
|
1451
1453
|
request += 1;
|
|
1452
1454
|
const requestStarted = performance.now();
|
|
1453
1455
|
try {
|
|
@@ -1506,6 +1508,7 @@ async function calibrateHttpConcurrency(options, fetcher = fetch, sampler = host
|
|
|
1506
1508
|
safetyRatio,
|
|
1507
1509
|
requestTimeoutMs: timeoutMs,
|
|
1508
1510
|
minimumStageDurationMs,
|
|
1511
|
+
maximumRequestsPerWorker,
|
|
1509
1512
|
maxCpuUtilizationPercent,
|
|
1510
1513
|
maxMemoryUtilizationPercent
|
|
1511
1514
|
} = validateCapacityCalibrationOptions(options);
|
|
@@ -1513,7 +1516,7 @@ async function calibrateHttpConcurrency(options, fetcher = fetch, sampler = host
|
|
|
1513
1516
|
let lastSafe;
|
|
1514
1517
|
let stopReason = "maximum-tested";
|
|
1515
1518
|
for (let concurrency = 1;; concurrency = Math.min(maxConcurrency, concurrency * 2)) {
|
|
1516
|
-
const measured = await stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimumStageDurationMs, fetcher, sampler);
|
|
1519
|
+
const measured = await stage(endpoint, concurrency, requestsPerWorker, maximumRequestsPerWorker, timeoutMs, minimumStageDurationMs, fetcher, sampler);
|
|
1517
1520
|
stages.push(measured);
|
|
1518
1521
|
const previous = stages.at(-2);
|
|
1519
1522
|
const throughputRegressed = Boolean(previous && concurrency > 1 && measured.successfulRequestsPerSecond < previous.successfulRequestsPerSecond * 0.9);
|
|
@@ -1614,6 +1617,7 @@ function capacityCalibration(value, where) {
|
|
|
1614
1617
|
"endpoint",
|
|
1615
1618
|
"maxConcurrency",
|
|
1616
1619
|
"requestsPerWorker",
|
|
1620
|
+
"maximumRequestsPerWorker",
|
|
1617
1621
|
"maxP95Ms",
|
|
1618
1622
|
"maxErrorRate",
|
|
1619
1623
|
"safetyRatio",
|
|
@@ -1642,6 +1646,7 @@ function capacityCalibration(value, where) {
|
|
|
1642
1646
|
...Object.fromEntries([
|
|
1643
1647
|
"maxConcurrency",
|
|
1644
1648
|
"requestsPerWorker",
|
|
1649
|
+
"maximumRequestsPerWorker",
|
|
1645
1650
|
"maxP95Ms",
|
|
1646
1651
|
"maxErrorRate",
|
|
1647
1652
|
"safetyRatio",
|
|
@@ -3004,7 +3009,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
3004
3009
|
}
|
|
3005
3010
|
|
|
3006
3011
|
// src/version.ts
|
|
3007
|
-
var VERSION3 = "0.1.
|
|
3012
|
+
var VERSION3 = "0.1.116";
|
|
3008
3013
|
|
|
3009
3014
|
// src/egress-policy.ts
|
|
3010
3015
|
import { realpathSync as realpathSync3 } from "node:fs";
|
|
@@ -4180,6 +4185,9 @@ function platformGenesisClaims(guests, sshPublicKeys) {
|
|
|
4180
4185
|
throw new Error("platform genesis requires at least one operator SSH public key");
|
|
4181
4186
|
return reviewed.map((guest) => claimFor(guest, keys, "create"));
|
|
4182
4187
|
}
|
|
4188
|
+
function platformGenesisDeletionClaims(guests) {
|
|
4189
|
+
return validatePlatformGenesisGuests(guests).map((guest) => claimFor(guest, [], "delete"));
|
|
4190
|
+
}
|
|
4183
4191
|
function platformCommunityRehearsalClaims(seedGuests, rehearsalGuest, sshPublicKeys) {
|
|
4184
4192
|
const reviewed = validatePlatformGenesisGuests([...seedGuests, rehearsalGuest], 4);
|
|
4185
4193
|
const keys = [...new Set(sshPublicKeys.map(publicKey))];
|
|
@@ -21,6 +21,8 @@ export interface PlatformGenesisGuest {
|
|
|
21
21
|
export declare function validatePlatformGenesisGuests(guests: readonly PlatformGenesisGuest[], expectedCount?: number): readonly PlatformGenesisGuest[];
|
|
22
22
|
/** Convert reviewed desired state into the ordinary Metal provisioning claim. */
|
|
23
23
|
export declare function platformGenesisClaims(guests: readonly PlatformGenesisGuest[], sshPublicKeys: readonly string[]): readonly RemoteProvisionClaim[];
|
|
24
|
+
/** Exact inverse of the reviewed three-node platform seed. */
|
|
25
|
+
export declare function platformGenesisDeletionClaims(guests: readonly PlatformGenesisGuest[]): readonly RemoteProvisionClaim[];
|
|
24
26
|
/** Add one explicitly reviewed disposable node to the same three guest requests. */
|
|
25
27
|
export declare function platformCommunityRehearsalClaims(seedGuests: readonly PlatformGenesisGuest[], rehearsalGuest: PlatformGenesisGuest, sshPublicKeys: readonly string[]): readonly RemoteProvisionClaim[];
|
|
26
28
|
/** Exact inverse of the reviewed disposable four-host request. */
|
package/dist/platform-genesis.js
CHANGED
|
@@ -669,6 +669,9 @@ function platformGenesisClaims(guests, sshPublicKeys) {
|
|
|
669
669
|
throw new Error("platform genesis requires at least one operator SSH public key");
|
|
670
670
|
return reviewed.map((guest) => claimFor(guest, keys, "create"));
|
|
671
671
|
}
|
|
672
|
+
function platformGenesisDeletionClaims(guests) {
|
|
673
|
+
return validatePlatformGenesisGuests(guests).map((guest) => claimFor(guest, [], "delete"));
|
|
674
|
+
}
|
|
672
675
|
function platformCommunityRehearsalClaims(seedGuests, rehearsalGuest, sshPublicKeys) {
|
|
673
676
|
const reviewed = validatePlatformGenesisGuests([...seedGuests, rehearsalGuest], 4);
|
|
674
677
|
const keys = [...new Set(sshPublicKeys.map(publicKey))];
|
|
@@ -681,6 +684,7 @@ function platformCommunityRehearsalDeletionClaims(seedGuests, rehearsalGuest) {
|
|
|
681
684
|
}
|
|
682
685
|
export {
|
|
683
686
|
validatePlatformGenesisGuests,
|
|
687
|
+
platformGenesisDeletionClaims,
|
|
684
688
|
platformGenesisClaims,
|
|
685
689
|
platformCommunityRehearsalDeletionClaims,
|
|
686
690
|
platformCommunityRehearsalClaims
|
package/dist/provision.js
CHANGED
|
@@ -1415,6 +1415,7 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
1415
1415
|
const endpoint = localCalibrationEndpoint(options.endpoint);
|
|
1416
1416
|
const maxConcurrency = options.maxConcurrency ?? 256;
|
|
1417
1417
|
const requestsPerWorker = options.requestsPerWorker ?? 8;
|
|
1418
|
+
const maximumRequestsPerWorker = options.maximumRequestsPerWorker ?? 100;
|
|
1418
1419
|
const maxP95Ms = options.maxP95Ms ?? 250;
|
|
1419
1420
|
const maxErrorRate = options.maxErrorRate ?? 0.01;
|
|
1420
1421
|
const safetyRatio = options.safetyRatio ?? 0.8;
|
|
@@ -1422,13 +1423,14 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
1422
1423
|
const minimumStageDurationMs = options.minimumStageDurationMs ?? 5000;
|
|
1423
1424
|
const maxCpuUtilizationPercent = options.maxCpuUtilizationPercent ?? 90;
|
|
1424
1425
|
const maxMemoryUtilizationPercent = options.maxMemoryUtilizationPercent ?? 90;
|
|
1425
|
-
if (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1 || maxConcurrency > 4096 || !Number.isSafeInteger(requestsPerWorker) || requestsPerWorker < 2 || requestsPerWorker > 100 || !Number.isFinite(maxP95Ms) || maxP95Ms < 1 || !Number.isFinite(maxErrorRate) || maxErrorRate < 0 || maxErrorRate > 0.2 || !Number.isFinite(safetyRatio) || safetyRatio < 0.25 || safetyRatio > 0.95 || !Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 100 || requestTimeoutMs > 30000 || !Number.isSafeInteger(minimumStageDurationMs) || minimumStageDurationMs < 100 || minimumStageDurationMs > 60000 || !Number.isFinite(maxCpuUtilizationPercent) || maxCpuUtilizationPercent < 25 || maxCpuUtilizationPercent > 100 || !Number.isFinite(maxMemoryUtilizationPercent) || maxMemoryUtilizationPercent < 25 || maxMemoryUtilizationPercent > 100) {
|
|
1426
|
+
if (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1 || maxConcurrency > 4096 || !Number.isSafeInteger(requestsPerWorker) || requestsPerWorker < 2 || requestsPerWorker > 100 || !Number.isSafeInteger(maximumRequestsPerWorker) || maximumRequestsPerWorker < requestsPerWorker || maximumRequestsPerWorker > 100 || !Number.isFinite(maxP95Ms) || maxP95Ms < 1 || !Number.isFinite(maxErrorRate) || maxErrorRate < 0 || maxErrorRate > 0.2 || !Number.isFinite(safetyRatio) || safetyRatio < 0.25 || safetyRatio > 0.95 || !Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 100 || requestTimeoutMs > 30000 || !Number.isSafeInteger(minimumStageDurationMs) || minimumStageDurationMs < 100 || minimumStageDurationMs > 60000 || !Number.isFinite(maxCpuUtilizationPercent) || maxCpuUtilizationPercent < 25 || maxCpuUtilizationPercent > 100 || !Number.isFinite(maxMemoryUtilizationPercent) || maxMemoryUtilizationPercent < 25 || maxMemoryUtilizationPercent > 100) {
|
|
1426
1427
|
throw new Error("Capacity calibration bounds are invalid.");
|
|
1427
1428
|
}
|
|
1428
1429
|
return {
|
|
1429
1430
|
endpoint,
|
|
1430
1431
|
maxConcurrency,
|
|
1431
1432
|
requestsPerWorker,
|
|
1433
|
+
maximumRequestsPerWorker,
|
|
1432
1434
|
maxP95Ms,
|
|
1433
1435
|
maxErrorRate,
|
|
1434
1436
|
safetyRatio,
|
|
@@ -1438,7 +1440,7 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
1438
1440
|
maxMemoryUtilizationPercent
|
|
1439
1441
|
};
|
|
1440
1442
|
}
|
|
1441
|
-
async function stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimumDurationMs, fetcher, sampler) {
|
|
1443
|
+
async function stage(endpoint, concurrency, requestsPerWorker, maximumRequestsPerWorker, timeoutMs, minimumDurationMs, fetcher, sampler) {
|
|
1442
1444
|
const latencies = [];
|
|
1443
1445
|
let succeeded = 0;
|
|
1444
1446
|
let failed = 0;
|
|
@@ -1447,7 +1449,7 @@ async function stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimu
|
|
|
1447
1449
|
const started = performance.now();
|
|
1448
1450
|
await Promise.all(Array.from({ length: concurrency }, async () => {
|
|
1449
1451
|
let request = 0;
|
|
1450
|
-
while (request < requestsPerWorker || performance.now() - started < minimumDurationMs) {
|
|
1452
|
+
while (request < maximumRequestsPerWorker && (request < requestsPerWorker || performance.now() - started < minimumDurationMs)) {
|
|
1451
1453
|
request += 1;
|
|
1452
1454
|
const requestStarted = performance.now();
|
|
1453
1455
|
try {
|
|
@@ -1506,6 +1508,7 @@ async function calibrateHttpConcurrency(options, fetcher = fetch, sampler = host
|
|
|
1506
1508
|
safetyRatio,
|
|
1507
1509
|
requestTimeoutMs: timeoutMs,
|
|
1508
1510
|
minimumStageDurationMs,
|
|
1511
|
+
maximumRequestsPerWorker,
|
|
1509
1512
|
maxCpuUtilizationPercent,
|
|
1510
1513
|
maxMemoryUtilizationPercent
|
|
1511
1514
|
} = validateCapacityCalibrationOptions(options);
|
|
@@ -1513,7 +1516,7 @@ async function calibrateHttpConcurrency(options, fetcher = fetch, sampler = host
|
|
|
1513
1516
|
let lastSafe;
|
|
1514
1517
|
let stopReason = "maximum-tested";
|
|
1515
1518
|
for (let concurrency = 1;; concurrency = Math.min(maxConcurrency, concurrency * 2)) {
|
|
1516
|
-
const measured = await stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimumStageDurationMs, fetcher, sampler);
|
|
1519
|
+
const measured = await stage(endpoint, concurrency, requestsPerWorker, maximumRequestsPerWorker, timeoutMs, minimumStageDurationMs, fetcher, sampler);
|
|
1517
1520
|
stages.push(measured);
|
|
1518
1521
|
const previous = stages.at(-2);
|
|
1519
1522
|
const throughputRegressed = Boolean(previous && concurrency > 1 && measured.successfulRequestsPerSecond < previous.successfulRequestsPerSecond * 0.9);
|
|
@@ -1614,6 +1617,7 @@ function capacityCalibration(value, where) {
|
|
|
1614
1617
|
"endpoint",
|
|
1615
1618
|
"maxConcurrency",
|
|
1616
1619
|
"requestsPerWorker",
|
|
1620
|
+
"maximumRequestsPerWorker",
|
|
1617
1621
|
"maxP95Ms",
|
|
1618
1622
|
"maxErrorRate",
|
|
1619
1623
|
"safetyRatio",
|
|
@@ -1642,6 +1646,7 @@ function capacityCalibration(value, where) {
|
|
|
1642
1646
|
...Object.fromEntries([
|
|
1643
1647
|
"maxConcurrency",
|
|
1644
1648
|
"requestsPerWorker",
|
|
1649
|
+
"maximumRequestsPerWorker",
|
|
1645
1650
|
"maxP95Ms",
|
|
1646
1651
|
"maxErrorRate",
|
|
1647
1652
|
"safetyRatio",
|
|
@@ -3004,7 +3009,7 @@ function requestSoftware(requirements, socketPath = DEFAULT_SOFTWARE_HELPER_SOCK
|
|
|
3004
3009
|
}
|
|
3005
3010
|
|
|
3006
3011
|
// src/version.ts
|
|
3007
|
-
var VERSION3 = "0.1.
|
|
3012
|
+
var VERSION3 = "0.1.116";
|
|
3008
3013
|
|
|
3009
3014
|
// src/egress-policy.ts
|
|
3010
3015
|
import { realpathSync as realpathSync3 } from "node:fs";
|
package/dist/software-helper.js
CHANGED
|
@@ -572,6 +572,7 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
572
572
|
const endpoint = localCalibrationEndpoint(options.endpoint);
|
|
573
573
|
const maxConcurrency = options.maxConcurrency ?? 256;
|
|
574
574
|
const requestsPerWorker = options.requestsPerWorker ?? 8;
|
|
575
|
+
const maximumRequestsPerWorker = options.maximumRequestsPerWorker ?? 100;
|
|
575
576
|
const maxP95Ms = options.maxP95Ms ?? 250;
|
|
576
577
|
const maxErrorRate = options.maxErrorRate ?? 0.01;
|
|
577
578
|
const safetyRatio = options.safetyRatio ?? 0.8;
|
|
@@ -579,13 +580,14 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
579
580
|
const minimumStageDurationMs = options.minimumStageDurationMs ?? 5000;
|
|
580
581
|
const maxCpuUtilizationPercent = options.maxCpuUtilizationPercent ?? 90;
|
|
581
582
|
const maxMemoryUtilizationPercent = options.maxMemoryUtilizationPercent ?? 90;
|
|
582
|
-
if (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1 || maxConcurrency > 4096 || !Number.isSafeInteger(requestsPerWorker) || requestsPerWorker < 2 || requestsPerWorker > 100 || !Number.isFinite(maxP95Ms) || maxP95Ms < 1 || !Number.isFinite(maxErrorRate) || maxErrorRate < 0 || maxErrorRate > 0.2 || !Number.isFinite(safetyRatio) || safetyRatio < 0.25 || safetyRatio > 0.95 || !Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 100 || requestTimeoutMs > 30000 || !Number.isSafeInteger(minimumStageDurationMs) || minimumStageDurationMs < 100 || minimumStageDurationMs > 60000 || !Number.isFinite(maxCpuUtilizationPercent) || maxCpuUtilizationPercent < 25 || maxCpuUtilizationPercent > 100 || !Number.isFinite(maxMemoryUtilizationPercent) || maxMemoryUtilizationPercent < 25 || maxMemoryUtilizationPercent > 100) {
|
|
583
|
+
if (!Number.isSafeInteger(maxConcurrency) || maxConcurrency < 1 || maxConcurrency > 4096 || !Number.isSafeInteger(requestsPerWorker) || requestsPerWorker < 2 || requestsPerWorker > 100 || !Number.isSafeInteger(maximumRequestsPerWorker) || maximumRequestsPerWorker < requestsPerWorker || maximumRequestsPerWorker > 100 || !Number.isFinite(maxP95Ms) || maxP95Ms < 1 || !Number.isFinite(maxErrorRate) || maxErrorRate < 0 || maxErrorRate > 0.2 || !Number.isFinite(safetyRatio) || safetyRatio < 0.25 || safetyRatio > 0.95 || !Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 100 || requestTimeoutMs > 30000 || !Number.isSafeInteger(minimumStageDurationMs) || minimumStageDurationMs < 100 || minimumStageDurationMs > 60000 || !Number.isFinite(maxCpuUtilizationPercent) || maxCpuUtilizationPercent < 25 || maxCpuUtilizationPercent > 100 || !Number.isFinite(maxMemoryUtilizationPercent) || maxMemoryUtilizationPercent < 25 || maxMemoryUtilizationPercent > 100) {
|
|
583
584
|
throw new Error("Capacity calibration bounds are invalid.");
|
|
584
585
|
}
|
|
585
586
|
return {
|
|
586
587
|
endpoint,
|
|
587
588
|
maxConcurrency,
|
|
588
589
|
requestsPerWorker,
|
|
590
|
+
maximumRequestsPerWorker,
|
|
589
591
|
maxP95Ms,
|
|
590
592
|
maxErrorRate,
|
|
591
593
|
safetyRatio,
|
|
@@ -595,7 +597,7 @@ function validateCapacityCalibrationOptions(options) {
|
|
|
595
597
|
maxMemoryUtilizationPercent
|
|
596
598
|
};
|
|
597
599
|
}
|
|
598
|
-
async function stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimumDurationMs, fetcher, sampler) {
|
|
600
|
+
async function stage(endpoint, concurrency, requestsPerWorker, maximumRequestsPerWorker, timeoutMs, minimumDurationMs, fetcher, sampler) {
|
|
599
601
|
const latencies = [];
|
|
600
602
|
let succeeded = 0;
|
|
601
603
|
let failed = 0;
|
|
@@ -604,7 +606,7 @@ async function stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimu
|
|
|
604
606
|
const started = performance.now();
|
|
605
607
|
await Promise.all(Array.from({ length: concurrency }, async () => {
|
|
606
608
|
let request = 0;
|
|
607
|
-
while (request < requestsPerWorker || performance.now() - started < minimumDurationMs) {
|
|
609
|
+
while (request < maximumRequestsPerWorker && (request < requestsPerWorker || performance.now() - started < minimumDurationMs)) {
|
|
608
610
|
request += 1;
|
|
609
611
|
const requestStarted = performance.now();
|
|
610
612
|
try {
|
|
@@ -663,6 +665,7 @@ async function calibrateHttpConcurrency(options, fetcher = fetch, sampler = host
|
|
|
663
665
|
safetyRatio,
|
|
664
666
|
requestTimeoutMs: timeoutMs,
|
|
665
667
|
minimumStageDurationMs,
|
|
668
|
+
maximumRequestsPerWorker,
|
|
666
669
|
maxCpuUtilizationPercent,
|
|
667
670
|
maxMemoryUtilizationPercent
|
|
668
671
|
} = validateCapacityCalibrationOptions(options);
|
|
@@ -670,7 +673,7 @@ async function calibrateHttpConcurrency(options, fetcher = fetch, sampler = host
|
|
|
670
673
|
let lastSafe;
|
|
671
674
|
let stopReason = "maximum-tested";
|
|
672
675
|
for (let concurrency = 1;; concurrency = Math.min(maxConcurrency, concurrency * 2)) {
|
|
673
|
-
const measured = await stage(endpoint, concurrency, requestsPerWorker, timeoutMs, minimumStageDurationMs, fetcher, sampler);
|
|
676
|
+
const measured = await stage(endpoint, concurrency, requestsPerWorker, maximumRequestsPerWorker, timeoutMs, minimumStageDurationMs, fetcher, sampler);
|
|
674
677
|
stages.push(measured);
|
|
675
678
|
const previous = stages.at(-2);
|
|
676
679
|
const throughputRegressed = Boolean(previous && concurrency > 1 && measured.successfulRequestsPerSecond < previous.successfulRequestsPerSecond * 0.9);
|
|
@@ -771,6 +774,7 @@ function capacityCalibration(value, where) {
|
|
|
771
774
|
"endpoint",
|
|
772
775
|
"maxConcurrency",
|
|
773
776
|
"requestsPerWorker",
|
|
777
|
+
"maximumRequestsPerWorker",
|
|
774
778
|
"maxP95Ms",
|
|
775
779
|
"maxErrorRate",
|
|
776
780
|
"safetyRatio",
|
|
@@ -799,6 +803,7 @@ function capacityCalibration(value, where) {
|
|
|
799
803
|
...Object.fromEntries([
|
|
800
804
|
"maxConcurrency",
|
|
801
805
|
"requestsPerWorker",
|
|
806
|
+
"maximumRequestsPerWorker",
|
|
802
807
|
"maxP95Ms",
|
|
803
808
|
"maxErrorRate",
|
|
804
809
|
"safetyRatio",
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** One package version shared by both public binaries. Pinned to package.json by tests. */
|
|
2
|
-
export declare const VERSION = "0.1.
|
|
2
|
+
export declare const VERSION = "0.1.116";
|