@loadstrike/loadstrike-sdk 1.0.30401 → 1.0.31601
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/README.md +6 -0
- package/dist/cjs/iteration-observation-diagnostics.js +513 -0
- package/dist/cjs/iteration-observations.js +195 -22
- package/dist/cjs/local-report-input.js +21 -0
- package/dist/cjs/local.js +55 -10
- package/dist/cjs/report-history.js +421 -0
- package/dist/cjs/reporting-svg.js +116 -0
- package/dist/cjs/reporting.js +436 -140
- package/dist/cjs/runtime.js +313 -85
- package/dist/cjs/sink-retry-policy.js +52 -0
- package/dist/cjs/sinks.js +112 -9
- package/dist/cjs/transports.js +78 -25
- package/dist/esm/iteration-observation-diagnostics.js +508 -0
- package/dist/esm/iteration-observations.js +195 -22
- package/dist/esm/local-report-input.js +17 -0
- package/dist/esm/local.js +55 -10
- package/dist/esm/report-history.js +413 -0
- package/dist/esm/reporting-svg.js +113 -0
- package/dist/esm/reporting.js +436 -140
- package/dist/esm/runtime.js +313 -85
- package/dist/esm/sink-retry-policy.js +44 -0
- package/dist/esm/sinks.js +112 -9
- package/dist/esm/transports.js +78 -25
- package/dist/types/iteration-observation-diagnostics.d.ts +21 -0
- package/dist/types/iteration-observations.d.ts +5 -0
- package/dist/types/local-report-input.d.ts +6 -0
- package/dist/types/report-history.d.ts +124 -0
- package/dist/types/reporting-svg.d.ts +2 -0
- package/dist/types/reporting.d.ts +6 -3
- package/dist/types/runtime.d.ts +25 -0
- package/dist/types/sink-retry-policy.d.ts +9 -0
- package/dist/types/sinks.d.ts +6 -0
- package/dist/types/transports.d.ts +4 -0
- package/package.json +2 -2
package/dist/cjs/runtime.js
CHANGED
|
@@ -13,9 +13,13 @@ const cluster_js_1 = require("./cluster.js");
|
|
|
13
13
|
const correlation_js_1 = require("./correlation.js");
|
|
14
14
|
const transports_js_1 = require("./transports.js");
|
|
15
15
|
const reporting_js_1 = require("./reporting.js");
|
|
16
|
+
const report_history_js_1 = require("./report-history.js");
|
|
17
|
+
const local_report_input_js_1 = require("./local-report-input.js");
|
|
16
18
|
const sinks_js_1 = require("./sinks.js");
|
|
17
19
|
const load_engine_v2_js_1 = require("./load-engine-v2.js");
|
|
18
20
|
const iteration_observations_js_1 = require("./iteration-observations.js");
|
|
21
|
+
const iteration_observation_diagnostics_js_1 = require("./iteration-observation-diagnostics.js");
|
|
22
|
+
const sink_retry_policy_js_1 = require("./sink-retry-policy.js");
|
|
19
23
|
const LOAD_ENGINE_V2_SCHEDULER_DISTRIBUTIONS = Symbol("loadstrike.load-engine-v2.scheduler-distributions");
|
|
20
24
|
const PORTAL_RUN_TOKEN_PROVIDER = Symbol.for("loadstrike.internal.portal-run-token-provider");
|
|
21
25
|
exports.LoadStrikeNodeType = {
|
|
@@ -289,6 +293,28 @@ class MeasurementAccumulator {
|
|
|
289
293
|
moreOrEq1200: left.moreOrEq1200 + right.moreOrEq1200
|
|
290
294
|
}, allRequestCount, durationMs);
|
|
291
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* Projects only the cumulative fields used by the private HTML-report
|
|
298
|
+
* history. Legacy distributions deliberately omit latency so this cadence
|
|
299
|
+
* path never scans their retained per-request arrays.
|
|
300
|
+
*/
|
|
301
|
+
buildReportHistoryMeasurement() {
|
|
302
|
+
return projectNativeReportHistoryMeasurement(this.count, this.allBytes, this.useHistogram ? this.latencyHistogram : undefined);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Combines only bounded native latency state plus exact counters. It does
|
|
306
|
+
* not materialize status rows or any public measurement DTO.
|
|
307
|
+
*/
|
|
308
|
+
buildCombinedReportHistoryMeasurement(other) {
|
|
309
|
+
const count = this.count + other.count;
|
|
310
|
+
const bytes = this.allBytes + other.allBytes;
|
|
311
|
+
if (!this.useHistogram || !other.useHistogram) {
|
|
312
|
+
return projectNativeReportHistoryMeasurement(count, bytes);
|
|
313
|
+
}
|
|
314
|
+
const latency = this.latencyHistogram.clone();
|
|
315
|
+
latency.merge(other.latencyHistogram);
|
|
316
|
+
return projectNativeReportHistoryMeasurement(count, bytes, latency);
|
|
317
|
+
}
|
|
292
318
|
histogramSnapshot() {
|
|
293
319
|
return {
|
|
294
320
|
count: this.count,
|
|
@@ -302,6 +328,24 @@ class MeasurementAccumulator {
|
|
|
302
328
|
};
|
|
303
329
|
}
|
|
304
330
|
}
|
|
331
|
+
function projectNativeReportHistoryMeasurement(count, bytes, latency) {
|
|
332
|
+
const measurement = {
|
|
333
|
+
count,
|
|
334
|
+
bytes,
|
|
335
|
+
approximate: latency?.maxRelativeError !== undefined
|
|
336
|
+
&& latency.maxRelativeError > 0
|
|
337
|
+
};
|
|
338
|
+
if (!latency || latency.count === 0n) {
|
|
339
|
+
return measurement;
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
...measurement,
|
|
343
|
+
percent50Ms: Number(latency.percentile(0.5)) / 1000,
|
|
344
|
+
percent75Ms: Number(latency.percentile(0.75)) / 1000,
|
|
345
|
+
percent95Ms: Number(latency.percentile(0.95)) / 1000,
|
|
346
|
+
percent99Ms: Number(latency.percentile(0.99)) / 1000
|
|
347
|
+
};
|
|
348
|
+
}
|
|
305
349
|
function buildHistogramMeasurement(snapshot, allRequestCount, durationMs) {
|
|
306
350
|
const count = snapshot.count;
|
|
307
351
|
const totalDurationMs = Math.max(durationMs, 0);
|
|
@@ -640,6 +684,21 @@ class ScenarioStatsAccumulator {
|
|
|
640
684
|
step.record(reply, observedLatencyMs);
|
|
641
685
|
return step.sortIndex;
|
|
642
686
|
}
|
|
687
|
+
/**
|
|
688
|
+
* Captures the bounded, private history view without building scenario
|
|
689
|
+
* steps, status rows, plugins, aliases, or any other public result shape.
|
|
690
|
+
*/
|
|
691
|
+
buildReportHistorySnapshot() {
|
|
692
|
+
const ok = this.ok.buildReportHistoryMeasurement();
|
|
693
|
+
const failed = this.fail.buildReportHistoryMeasurement();
|
|
694
|
+
return {
|
|
695
|
+
scenarioName: this.scenarioName,
|
|
696
|
+
sortIndex: this.sortIndex,
|
|
697
|
+
all: this.ok.buildCombinedReportHistoryMeasurement(this.fail),
|
|
698
|
+
...(ok.count > 0 ? { ok } : {}),
|
|
699
|
+
...(failed.count > 0 ? { failed } : {})
|
|
700
|
+
};
|
|
701
|
+
}
|
|
643
702
|
/**
|
|
644
703
|
* Builds the configured payload or helper object.
|
|
645
704
|
* Use this when all builder inputs are ready to be materialized.
|
|
@@ -3455,8 +3514,8 @@ class LoadStrikeRunner {
|
|
|
3455
3514
|
}));
|
|
3456
3515
|
const sinkErrors = [];
|
|
3457
3516
|
const policyErrors = [];
|
|
3458
|
-
const sinkRetryCount =
|
|
3459
|
-
const sinkRetryBackoffMs =
|
|
3517
|
+
const sinkRetryCount = (0, sink_retry_policy_js_1.normalizeSinkRetryCount)(this.options.sinkRetryCount);
|
|
3518
|
+
const sinkRetryBackoffMs = (0, sink_retry_policy_js_1.normalizeSinkRetryBackoffMs)(this.options.sinkRetryBackoffMs);
|
|
3460
3519
|
const policies = this.options.runtimePolicies ?? [];
|
|
3461
3520
|
const runtimePolicyErrorMode = normalizedRuntimePolicyErrorMode(this.options.runtimePolicyErrorMode);
|
|
3462
3521
|
const plugins = this.options.reportingSinks === undefined && this.options.workerPlugins === undefined &&
|
|
@@ -3507,6 +3566,58 @@ class LoadStrikeRunner {
|
|
|
3507
3566
|
licenseSession = await licenseClient.acquireLicenseLease(licensePayload);
|
|
3508
3567
|
const loggerSetup = createLoggerSetup(this.options.loggerConfig, this.options.minimumLogLevel, this.options, testInfo, nodeInfo);
|
|
3509
3568
|
const runLogger = loggerSetup.logger;
|
|
3569
|
+
const htmlReportHistoryRequested = (this.options.reportsEnabled ?? true)
|
|
3570
|
+
&& normalizeReportFormats(this.options.reportFormats ?? ["html", "txt", "csv", "md"]).includes("html");
|
|
3571
|
+
const distributedReportHistory = isDistributedReportHistoryExecution(clusterMode, this.options);
|
|
3572
|
+
let localReportInput = htmlReportHistoryRequested
|
|
3573
|
+
&& distributedReportHistory
|
|
3574
|
+
? (0, local_report_input_js_1.distributedLocalReportInput)()
|
|
3575
|
+
: (0, local_report_input_js_1.emptyLocalReportInput)();
|
|
3576
|
+
let reportHistoryCollector;
|
|
3577
|
+
let reportHistoryLifecycle;
|
|
3578
|
+
if (htmlReportHistoryRequested && !distributedReportHistory) {
|
|
3579
|
+
const warnReportHistoryFailure = (category, error) => {
|
|
3580
|
+
const exceptionClasses = error === undefined
|
|
3581
|
+
? ""
|
|
3582
|
+
: `; exception_classes=${(0, report_history_js_1.sanitizedExceptionClassChain)(error)}`;
|
|
3583
|
+
try {
|
|
3584
|
+
runLogger.warn(`LoadStrike report history unavailable: ${category}${exceptionClasses}.`);
|
|
3585
|
+
}
|
|
3586
|
+
catch {
|
|
3587
|
+
// Report-only diagnostics are best-effort.
|
|
3588
|
+
}
|
|
3589
|
+
};
|
|
3590
|
+
reportHistoryCollector = new report_history_js_1.ReportHistoryCollector({
|
|
3591
|
+
scenarioCount: selectedScenarios.length,
|
|
3592
|
+
onCaptureError: (error) => warnReportHistoryFailure("capture_failure", error)
|
|
3593
|
+
});
|
|
3594
|
+
if (!reportHistoryCollector.available) {
|
|
3595
|
+
warnReportHistoryFailure("budget_pressure");
|
|
3596
|
+
}
|
|
3597
|
+
const snapshotFactory = () => Array.from(scenarioAccumulators.values())
|
|
3598
|
+
.map((value) => value.buildReportHistorySnapshot())
|
|
3599
|
+
.sort((left, right) => (left.sortIndex ?? 0) - (right.sortIndex ?? 0)
|
|
3600
|
+
|| left.scenarioName.localeCompare(right.scenarioName));
|
|
3601
|
+
const reportHistoryWorker = new report_history_js_1.ReportHistoryWorker({
|
|
3602
|
+
collector: reportHistoryCollector,
|
|
3603
|
+
cadenceSeconds: resolvedReportHistoryCadenceSeconds(this.options.reportingIntervalSeconds ?? 5),
|
|
3604
|
+
snapshotFactory,
|
|
3605
|
+
onFailure: (category, exceptionClasses) => {
|
|
3606
|
+
try {
|
|
3607
|
+
runLogger.warn(`LoadStrike report history unavailable: ${category}; exception_classes=${exceptionClasses}.`);
|
|
3608
|
+
}
|
|
3609
|
+
catch {
|
|
3610
|
+
// Report-only diagnostics are best-effort.
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
});
|
|
3614
|
+
reportHistoryLifecycle = new report_history_js_1.ReportHistoryLifecycleCoordinator({
|
|
3615
|
+
scenarioCount: selectedScenarios.length,
|
|
3616
|
+
start: () => reportHistoryWorker.start(),
|
|
3617
|
+
stopAndFinalize: () => reportHistoryWorker.stopAndFinalize(),
|
|
3618
|
+
stopWithoutFinalizing: () => reportHistoryWorker.stopWithoutFinalizing()
|
|
3619
|
+
});
|
|
3620
|
+
}
|
|
3510
3621
|
const scenarioStartInfos = selectedScenarios.map((scenario, index) => {
|
|
3511
3622
|
const startInfo = {
|
|
3512
3623
|
scenarioName: scenario.name,
|
|
@@ -3550,14 +3661,14 @@ class LoadStrikeRunner {
|
|
|
3550
3661
|
await init(baseContext, this.options.infraConfig ?? {});
|
|
3551
3662
|
}
|
|
3552
3663
|
}
|
|
3553
|
-
await this.initializeSinks(sinkStates, baseContext, this.options.infraConfig ?? {}, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3664
|
+
await this.initializeSinks(sinkStates, baseContext, this.options.infraConfig ?? {}, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3554
3665
|
for (const plugin of plugins) {
|
|
3555
3666
|
const start = resolveWorkerPluginStart(plugin);
|
|
3556
3667
|
if (start) {
|
|
3557
3668
|
await start(sessionInfo);
|
|
3558
3669
|
}
|
|
3559
3670
|
}
|
|
3560
|
-
await this.startSinks(sinkStates, sessionInfo, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3671
|
+
await this.startSinks(sinkStates, sessionInfo, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3561
3672
|
const iterationObservationRunId = String(this.internalOptions.iterationObservationRunId
|
|
3562
3673
|
?? sessionInfo.portalReportingRunId
|
|
3563
3674
|
?? sessionInfo.PortalReportingRunId
|
|
@@ -3588,7 +3699,10 @@ class LoadStrikeRunner {
|
|
|
3588
3699
|
expectedResultOwnerCount64: iterationObservationExpectedResultOwnerCount64,
|
|
3589
3700
|
processGroup: iterationObservationProcessGroup,
|
|
3590
3701
|
settings: resolveIterationObservationSettings(this.options),
|
|
3591
|
-
sinks: reporterIterationObservationSinks
|
|
3702
|
+
sinks: reporterIterationObservationSinks,
|
|
3703
|
+
sinkRetryCount,
|
|
3704
|
+
sinkRetryBackoffMs,
|
|
3705
|
+
logger: runLogger
|
|
3592
3706
|
});
|
|
3593
3707
|
const emitRealtimeSnapshot = async () => {
|
|
3594
3708
|
if (realtimeInFlight) {
|
|
@@ -3600,7 +3714,7 @@ class LoadStrikeRunner {
|
|
|
3600
3714
|
.map((value) => value.build(scenarioDurationsMs.get(value.scenarioName) ?? Math.max(Date.now() - started.getTime(), 0)))
|
|
3601
3715
|
.sort((left, right) => left.sortIndex - right.sortIndex);
|
|
3602
3716
|
const metricsSnapshot = collectMetricStats(allRegisteredMetrics, Date.now() - started.getTime());
|
|
3603
|
-
await this.emitRealtimeStats(sinkStates, snapshot, metricsSnapshot, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3717
|
+
await this.emitRealtimeStats(sinkStates, snapshot, metricsSnapshot, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3604
3718
|
if (toBoolean(this.options.displayConsoleMetrics, true)) {
|
|
3605
3719
|
const requestCount = snapshot.reduce((sum, value) => sum + value.allRequestCount, 0);
|
|
3606
3720
|
const okCount = snapshot.reduce((sum, value) => sum + value.allOkCount, 0);
|
|
@@ -3613,7 +3727,7 @@ class LoadStrikeRunner {
|
|
|
3613
3727
|
realtimeInFlight = false;
|
|
3614
3728
|
}
|
|
3615
3729
|
};
|
|
3616
|
-
const reportingIntervalMs =
|
|
3730
|
+
const reportingIntervalMs = boundedReportingIntervalMs(this.options.reportingIntervalSeconds ?? 5);
|
|
3617
3731
|
const triggerRealtimeSnapshot = () => {
|
|
3618
3732
|
if (realtimeCurrent) {
|
|
3619
3733
|
return;
|
|
@@ -3700,6 +3814,7 @@ class LoadStrikeRunner {
|
|
|
3700
3814
|
iterationObservationRunId,
|
|
3701
3815
|
iterationObservationResultOwnerId,
|
|
3702
3816
|
iterationObservationProcessGroup,
|
|
3817
|
+
reportHistoryLifecycle,
|
|
3703
3818
|
executeScenarioInvocation: (targetScenario, context, operation) => this.executeScenarioInvocation(targetScenario, context, operation),
|
|
3704
3819
|
invokeBeforeScenario: (runtimePolicies, scenarioName) => this.invokeBeforeScenario(runtimePolicies, scenarioName, policyErrors, runtimePolicyErrorMode),
|
|
3705
3820
|
invokeAfterScenario: (runtimePolicies, scenarioName, stats) => this.invokeAfterScenario(runtimePolicies, scenarioName, stats, policyErrors, runtimePolicyErrorMode),
|
|
@@ -3787,12 +3902,17 @@ class LoadStrikeRunner {
|
|
|
3787
3902
|
...iterationObservationReporter.buildWarnings()
|
|
3788
3903
|
];
|
|
3789
3904
|
result.pluginsData = mergePluginData(result.pluginsData, await this.collectPluginData(plugins, attachRunResultAliases(result), pluginLifecycleErrors));
|
|
3905
|
+
if (reportHistoryCollector) {
|
|
3906
|
+
localReportInput = {
|
|
3907
|
+
history: reportHistoryCollector.toProjection()
|
|
3908
|
+
};
|
|
3909
|
+
}
|
|
3790
3910
|
const finalizedResult = attachRunResultAliases(result);
|
|
3791
3911
|
finalizedResult.logFiles = mergeStringArrays(finalizedResult.logFiles, loggerSetup.logFiles);
|
|
3792
|
-
finalizedResult.reportFiles = this.writeReports(finalizedResult);
|
|
3912
|
+
finalizedResult.reportFiles = this.writeReports(finalizedResult, localReportInput);
|
|
3793
3913
|
finalizedResult.disabledSinks = sinkStates.filter((x) => x.disabled).map((x) => x.name);
|
|
3794
|
-
await this.emitRunResult(sinkStates, finalizedResult, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3795
|
-
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3914
|
+
await this.emitRunResult(sinkStates, finalizedResult, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3915
|
+
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3796
3916
|
sinksStopped = true;
|
|
3797
3917
|
finalizedResult.disabledSinks = sinkStates.filter((x) => x.disabled).map((x) => x.name);
|
|
3798
3918
|
finalizedResult.sinkErrors = sinkErrors
|
|
@@ -3800,12 +3920,13 @@ class LoadStrikeRunner {
|
|
|
3800
3920
|
return finalizedResult;
|
|
3801
3921
|
}
|
|
3802
3922
|
finally {
|
|
3923
|
+
reportHistoryLifecycle?.stopWithoutFinalizing();
|
|
3803
3924
|
await stopRealtimeReporting();
|
|
3804
3925
|
if (!iterationObservationsFinalized) {
|
|
3805
3926
|
await iterationObservationReporter.sealAndDrain().catch(() => { });
|
|
3806
3927
|
}
|
|
3807
3928
|
if (!sinksStopped) {
|
|
3808
|
-
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3929
|
+
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3809
3930
|
}
|
|
3810
3931
|
if (!pluginsStopped) {
|
|
3811
3932
|
await this.stopPlugins(plugins, pluginLifecycleErrors, runLogger);
|
|
@@ -4113,9 +4234,9 @@ class LoadStrikeRunner {
|
|
|
4113
4234
|
}
|
|
4114
4235
|
return filtered;
|
|
4115
4236
|
}
|
|
4116
|
-
async initializeSinks(sinkStates, context, infraConfig, retryCount, retryBackoffMs, sinkErrors) {
|
|
4237
|
+
async initializeSinks(sinkStates, context, infraConfig, retryCount, retryBackoffMs, sinkErrors, runId, logger) {
|
|
4117
4238
|
for (const state of sinkStates) {
|
|
4118
|
-
await this.invokeSinkAction(state, "init", retryCount, retryBackoffMs, sinkErrors, false, true, async () => {
|
|
4239
|
+
await this.invokeSinkAction(state, "init", retryCount, retryBackoffMs, sinkErrors, runId, logger, false, true, async () => {
|
|
4119
4240
|
const init = resolveSinkInit(state.sink);
|
|
4120
4241
|
if (init) {
|
|
4121
4242
|
await init(context, infraConfig);
|
|
@@ -4123,9 +4244,9 @@ class LoadStrikeRunner {
|
|
|
4123
4244
|
});
|
|
4124
4245
|
}
|
|
4125
4246
|
}
|
|
4126
|
-
async startSinks(sinkStates, session, retryCount, retryBackoffMs, sinkErrors) {
|
|
4247
|
+
async startSinks(sinkStates, session, retryCount, retryBackoffMs, sinkErrors, runId, logger) {
|
|
4127
4248
|
for (const state of sinkStates) {
|
|
4128
|
-
await this.invokeSinkAction(state, "start", retryCount, retryBackoffMs, sinkErrors, false, true, async () => {
|
|
4249
|
+
await this.invokeSinkAction(state, "start", retryCount, retryBackoffMs, sinkErrors, runId, logger, false, true, async () => {
|
|
4129
4250
|
const start = resolveSinkStart(state.sink);
|
|
4130
4251
|
if (start) {
|
|
4131
4252
|
await start(session);
|
|
@@ -4133,9 +4254,9 @@ class LoadStrikeRunner {
|
|
|
4133
4254
|
});
|
|
4134
4255
|
}
|
|
4135
4256
|
}
|
|
4136
|
-
async emitRealtimeStats(sinkStates, scenarioStats, metrics, retryCount, retryBackoffMs, sinkErrors) {
|
|
4257
|
+
async emitRealtimeStats(sinkStates, scenarioStats, metrics, retryCount, retryBackoffMs, sinkErrors, runId, logger) {
|
|
4137
4258
|
for (const state of sinkStates) {
|
|
4138
|
-
await this.invokeSinkAction(state, "realtime", retryCount, retryBackoffMs, sinkErrors, false, true, async () => {
|
|
4259
|
+
await this.invokeSinkAction(state, "realtime", retryCount, retryBackoffMs, sinkErrors, runId, logger, false, true, async () => {
|
|
4139
4260
|
const saveRealtimeStats = resolveSinkSaveRealtimeStats(state.sink);
|
|
4140
4261
|
if (saveRealtimeStats) {
|
|
4141
4262
|
await saveRealtimeStats(scenarioStats);
|
|
@@ -4147,10 +4268,9 @@ class LoadStrikeRunner {
|
|
|
4147
4268
|
});
|
|
4148
4269
|
}
|
|
4149
4270
|
}
|
|
4150
|
-
async stopSinks(sinkStates, retryCount, retryBackoffMs, sinkErrors) {
|
|
4151
|
-
const shutdownRetryCount = 0;
|
|
4271
|
+
async stopSinks(sinkStates, retryCount, retryBackoffMs, sinkErrors, runId, logger) {
|
|
4152
4272
|
for (const state of sinkStates) {
|
|
4153
|
-
await this.invokeSinkAction(state, "stop",
|
|
4273
|
+
await this.invokeSinkAction(state, "stop", retryCount, retryBackoffMs, sinkErrors, runId, logger, true, true, async () => {
|
|
4154
4274
|
const stop = resolveSinkStop(state.sink);
|
|
4155
4275
|
if (stop) {
|
|
4156
4276
|
await stop();
|
|
@@ -4158,15 +4278,15 @@ class LoadStrikeRunner {
|
|
|
4158
4278
|
});
|
|
4159
4279
|
const dispose = resolveSinkDispose(state.sink);
|
|
4160
4280
|
if (dispose) {
|
|
4161
|
-
await this.invokeSinkAction(state, "dispose",
|
|
4281
|
+
await this.invokeSinkAction(state, "dispose", retryCount, retryBackoffMs, sinkErrors, runId, logger, true, true, async () => {
|
|
4162
4282
|
await dispose();
|
|
4163
4283
|
});
|
|
4164
4284
|
}
|
|
4165
4285
|
}
|
|
4166
4286
|
}
|
|
4167
|
-
async emitRunResult(sinkStates, result, retryCount, retryBackoffMs, sinkErrors) {
|
|
4287
|
+
async emitRunResult(sinkStates, result, retryCount, retryBackoffMs, sinkErrors, runId, logger) {
|
|
4168
4288
|
for (const state of sinkStates) {
|
|
4169
|
-
await this.invokeSinkAction(state, "run-result", retryCount, retryBackoffMs, sinkErrors, false, false, async () => {
|
|
4289
|
+
await this.invokeSinkAction(state, "run-result", retryCount, retryBackoffMs, sinkErrors, runId, logger, false, false, async () => {
|
|
4170
4290
|
const saveRunResult = resolveSinkSaveRunResult(state.sink);
|
|
4171
4291
|
if (saveRunResult) {
|
|
4172
4292
|
await saveRunResult(result);
|
|
@@ -4174,23 +4294,49 @@ class LoadStrikeRunner {
|
|
|
4174
4294
|
});
|
|
4175
4295
|
}
|
|
4176
4296
|
}
|
|
4177
|
-
async invokeSinkAction(state, phase, retryCount, retryBackoffMs, sinkErrors, ignoreDisabled, disableOnFailure, action) {
|
|
4297
|
+
async invokeSinkAction(state, phase, retryCount, retryBackoffMs, sinkErrors, runId, logger, ignoreDisabled, disableOnFailure, action) {
|
|
4178
4298
|
if (state.disabled && !ignoreDisabled) {
|
|
4179
4299
|
return;
|
|
4180
4300
|
}
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4301
|
+
const maximumAttempts = (0, sink_retry_policy_js_1.normalizeSinkRetryCount)(retryCount) + 1;
|
|
4302
|
+
const backoffMs = (0, sink_retry_policy_js_1.normalizeSinkRetryBackoffMs)(retryBackoffMs);
|
|
4303
|
+
for (let attempts = 1; attempts <= maximumAttempts; attempts += 1) {
|
|
4184
4304
|
try {
|
|
4185
4305
|
await action();
|
|
4306
|
+
if (attempts > 1) {
|
|
4307
|
+
(0, iteration_observation_diagnostics_js_1.logIterationObservationRecovery)(logger, {
|
|
4308
|
+
sinkName: state.name,
|
|
4309
|
+
operation: "reporting-sink-action",
|
|
4310
|
+
phase,
|
|
4311
|
+
runId,
|
|
4312
|
+
resultOwnerId: "",
|
|
4313
|
+
observationCount: 0,
|
|
4314
|
+
attempt: attempts,
|
|
4315
|
+
maximumAttempts,
|
|
4316
|
+
nextDelayMs: 0
|
|
4317
|
+
});
|
|
4318
|
+
}
|
|
4186
4319
|
return;
|
|
4187
4320
|
}
|
|
4188
4321
|
catch (error) {
|
|
4189
|
-
|
|
4322
|
+
const exhausted = attempts >= maximumAttempts;
|
|
4323
|
+
const nextDelayMs = exhausted ? 0 : (0, sink_retry_policy_js_1.sinkRetryDelayMs)(backoffMs, attempts);
|
|
4324
|
+
(0, iteration_observation_diagnostics_js_1.logIterationObservationFailure)(logger, exhausted ? "error" : "warn", {
|
|
4325
|
+
sinkName: state.name,
|
|
4326
|
+
operation: "reporting-sink-action",
|
|
4327
|
+
phase,
|
|
4328
|
+
runId,
|
|
4329
|
+
resultOwnerId: "",
|
|
4330
|
+
observationCount: 0,
|
|
4331
|
+
attempt: attempts,
|
|
4332
|
+
maximumAttempts,
|
|
4333
|
+
nextDelayMs
|
|
4334
|
+
}, error);
|
|
4335
|
+
if (exhausted) {
|
|
4190
4336
|
sinkErrors.push({
|
|
4191
4337
|
sinkName: state.name,
|
|
4192
4338
|
phase,
|
|
4193
|
-
message:
|
|
4339
|
+
message: "The reporting sink action failed after retries.",
|
|
4194
4340
|
attempts
|
|
4195
4341
|
});
|
|
4196
4342
|
if (disableOnFailure) {
|
|
@@ -4198,9 +4344,7 @@ class LoadStrikeRunner {
|
|
|
4198
4344
|
}
|
|
4199
4345
|
return;
|
|
4200
4346
|
}
|
|
4201
|
-
|
|
4202
|
-
await sleep(retryBackoffMs * attempts);
|
|
4203
|
-
}
|
|
4347
|
+
await (0, sink_retry_policy_js_1.waitForSinkRetryDelay)(nextDelayMs);
|
|
4204
4348
|
}
|
|
4205
4349
|
}
|
|
4206
4350
|
}
|
|
@@ -4254,7 +4398,7 @@ class LoadStrikeRunner {
|
|
|
4254
4398
|
message: String(error ?? "runtime policy callback failed")
|
|
4255
4399
|
});
|
|
4256
4400
|
}
|
|
4257
|
-
writeReports(result) {
|
|
4401
|
+
writeReports(result, localReportInput = (0, local_report_input_js_1.emptyLocalReportInput)()) {
|
|
4258
4402
|
const reportsEnabled = this.options.reportsEnabled ?? true;
|
|
4259
4403
|
if (!reportsEnabled) {
|
|
4260
4404
|
return [];
|
|
@@ -4279,7 +4423,7 @@ class LoadStrikeRunner {
|
|
|
4279
4423
|
(0, node_fs_1.writeFileSync)(path, (0, reporting_js_1.buildDotnetMarkdownReport)(nodeStats), "utf8");
|
|
4280
4424
|
}
|
|
4281
4425
|
else if (format === "html") {
|
|
4282
|
-
(0, node_fs_1.writeFileSync)(path, (0, reporting_js_1.buildDotnetHtmlReport)(nodeStats), "utf8");
|
|
4426
|
+
(0, node_fs_1.writeFileSync)(path, (0, reporting_js_1.buildDotnetHtmlReport)(nodeStats, localReportInput), "utf8");
|
|
4283
4427
|
}
|
|
4284
4428
|
written.push(path);
|
|
4285
4429
|
}
|
|
@@ -4313,6 +4457,19 @@ function hasPluginRows(value) {
|
|
|
4313
4457
|
}
|
|
4314
4458
|
return value.tables.some((table) => Array.isArray(table.rows) && table.rows.length > 0);
|
|
4315
4459
|
}
|
|
4460
|
+
function boundedReportingIntervalMs(seconds) {
|
|
4461
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
4462
|
+
return 1;
|
|
4463
|
+
}
|
|
4464
|
+
const milliseconds = seconds * 1000;
|
|
4465
|
+
if (!Number.isFinite(milliseconds) || milliseconds >= 2147483647) {
|
|
4466
|
+
return 2147483647;
|
|
4467
|
+
}
|
|
4468
|
+
return Math.max(Math.trunc(milliseconds), 1);
|
|
4469
|
+
}
|
|
4470
|
+
function resolvedReportHistoryCadenceSeconds(seconds) {
|
|
4471
|
+
return boundedReportingIntervalMs(seconds) / 1000;
|
|
4472
|
+
}
|
|
4316
4473
|
async function executeV2FixedArrivals(args) {
|
|
4317
4474
|
const { rate, intervalNs, totalArrivals, budget, cancellationToken, shouldStopNow, nextInstanceInfo, runBombingInvocation, logger, scenarioName, deadlineOffsetsNs, tolerancesNs, ownedOrdinals, shardIndex = 0, shardCount = 1, telemetry, segment } = args;
|
|
4318
4475
|
const segmentStartNs = process.hrtime.bigint();
|
|
@@ -4449,7 +4606,15 @@ function maxBigInt(left, right) {
|
|
|
4449
4606
|
return left > right ? left : right;
|
|
4450
4607
|
}
|
|
4451
4608
|
async function executeScenarioRuntime(args) {
|
|
4452
|
-
const { scenario, scenarioIndex, scenarioCount, options, logger, nodeInfo, testInfo, policies, restartIterationMaxAttempts, allRegisteredMetrics, scenarioRuntimes, stepRuntimes, scenarioAccumulators, scenarioDurationsMs, stopTestState, testAbortController, loadEngineV2Budget, loadEngineV2Telemetry, iterationObservationReporter, iterationObservationRunId = testInfo.sessionId, iterationObservationResultOwnerId = "", iterationObservationProcessGroup = 0, executeScenarioInvocation, invokeBeforeScenario, invokeAfterScenario, invokeBeforeStep, invokeAfterStep } = args;
|
|
4609
|
+
const { scenario, scenarioIndex, scenarioCount, options, logger, nodeInfo, testInfo, policies, restartIterationMaxAttempts, allRegisteredMetrics, scenarioRuntimes, stepRuntimes, scenarioAccumulators, scenarioDurationsMs, stopTestState, testAbortController, loadEngineV2Budget, loadEngineV2Telemetry, iterationObservationReporter, iterationObservationRunId = testInfo.sessionId, iterationObservationResultOwnerId = "", iterationObservationProcessGroup = 0, reportHistoryLifecycle, executeScenarioInvocation, invokeBeforeScenario, invokeAfterScenario, invokeBeforeStep, invokeAfterStep } = args;
|
|
4610
|
+
let reportHistoryExecutionEnded = false;
|
|
4611
|
+
const endReportHistoryExecution = async () => {
|
|
4612
|
+
if (reportHistoryExecutionEnded) {
|
|
4613
|
+
return;
|
|
4614
|
+
}
|
|
4615
|
+
reportHistoryExecutionEnded = true;
|
|
4616
|
+
await reportHistoryLifecycle?.scenarioExecutionEnded();
|
|
4617
|
+
};
|
|
4453
4618
|
const scenarioStartedMs = Date.now();
|
|
4454
4619
|
const scenarioContextData = {};
|
|
4455
4620
|
const registeredMetrics = [];
|
|
@@ -4610,7 +4775,18 @@ async function executeScenarioRuntime(args) {
|
|
|
4610
4775
|
attachScenarioContextAliases(context);
|
|
4611
4776
|
const startedUtcNs = (0, iteration_observations_js_1.utcNowNs)();
|
|
4612
4777
|
const startedAtNs = process.hrtime.bigint();
|
|
4613
|
-
|
|
4778
|
+
let policyFailure;
|
|
4779
|
+
let reply;
|
|
4780
|
+
try {
|
|
4781
|
+
reply = await executeScenarioInvocation(scenario, context, operation);
|
|
4782
|
+
}
|
|
4783
|
+
catch (error) {
|
|
4784
|
+
if (!(error instanceof RuntimePolicyCallbackError)) {
|
|
4785
|
+
throw error;
|
|
4786
|
+
}
|
|
4787
|
+
policyFailure = error;
|
|
4788
|
+
reply = LoadStrikeResponse.fail("runtime_policy_error", "", 0);
|
|
4789
|
+
}
|
|
4614
4790
|
const observedLatencyNs = maxBigInt(process.hrtime.bigint() - startedAtNs, 0n);
|
|
4615
4791
|
const completedUtcNs = startedUtcNs + observedLatencyNs;
|
|
4616
4792
|
const observedLatencyMs = Number(observedLatencyNs) / 1000000;
|
|
@@ -4625,7 +4801,8 @@ async function executeScenarioRuntime(args) {
|
|
|
4625
4801
|
observedLatencyUs: observedLatencyNs / 1000n,
|
|
4626
4802
|
reportedLatencyUs: normalizeRawObservationLatencyMicroseconds(resolveRecordedLatency(reply.customLatencyMs, observedLatencyMs)),
|
|
4627
4803
|
steps: attemptSteps,
|
|
4628
|
-
recordedSteps
|
|
4804
|
+
recordedSteps,
|
|
4805
|
+
...(policyFailure ? { policyFailure } : {})
|
|
4629
4806
|
};
|
|
4630
4807
|
};
|
|
4631
4808
|
const captureAttemptObservation = (operation, globalOrdinal, attemptIndex, isFinalAttempt, attempt, simulationIndex, simulationKind, iterationId, globalSecondaryOrdinal = 0n) => {
|
|
@@ -4674,22 +4851,18 @@ async function executeScenarioRuntime(args) {
|
|
|
4674
4851
|
const maxAttempts = 1 + (scenario.shouldRestartIterationOnFail() ? restartIterationMaxAttempts : 0);
|
|
4675
4852
|
while (attempts < maxAttempts && !shouldStopNow()) {
|
|
4676
4853
|
attempts += 1;
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
attempt
|
|
4680
|
-
}
|
|
4681
|
-
catch (error) {
|
|
4682
|
-
if (error instanceof RuntimePolicyCallbackError) {
|
|
4683
|
-
stopScenario = true;
|
|
4684
|
-
scenarioAbortController.abort(error);
|
|
4685
|
-
}
|
|
4686
|
-
throw error;
|
|
4687
|
-
}
|
|
4688
|
-
const shouldRetry = !attempt.reply.isSuccess
|
|
4854
|
+
const attempt = await runSingleInvocation("Bombing", instanceData, instanceNumber, instanceId, false);
|
|
4855
|
+
const shouldRetry = !attempt.policyFailure
|
|
4856
|
+
&& !attempt.reply.isSuccess
|
|
4689
4857
|
&& scenario.shouldRestartIterationOnFail()
|
|
4690
4858
|
&& attempts < maxAttempts
|
|
4691
4859
|
&& !shouldStopNow();
|
|
4692
4860
|
captureAttemptObservation("Bombing", globalOrdinal, attempts - 1, !shouldRetry, attempt, simulationIndex, simulationKind, iterationId, explicitSecondaryOrdinal);
|
|
4861
|
+
if (attempt.policyFailure) {
|
|
4862
|
+
stopScenario = true;
|
|
4863
|
+
scenarioAbortController.abort(attempt.policyFailure);
|
|
4864
|
+
throw attempt.policyFailure;
|
|
4865
|
+
}
|
|
4693
4866
|
if (!shouldRetry) {
|
|
4694
4867
|
for (const step of attempt.recordedSteps) {
|
|
4695
4868
|
recordStepReply(step.stepName, step.reply, step.observedLatencyMs, step.sortIndex);
|
|
@@ -4716,6 +4889,11 @@ async function executeScenarioRuntime(args) {
|
|
|
4716
4889
|
const globalOrdinal = nextObservationOrdinal();
|
|
4717
4890
|
const attempt = await runSingleInvocation("WarmUp", instanceInfo.instanceData, instanceInfo.instanceNumber, instanceInfo.instanceId, false);
|
|
4718
4891
|
captureAttemptObservation("WarmUp", globalOrdinal, 0, true, attempt, -1, "SingleInvocation");
|
|
4892
|
+
if (attempt.policyFailure) {
|
|
4893
|
+
stopScenario = true;
|
|
4894
|
+
scenarioAbortController.abort(attempt.policyFailure);
|
|
4895
|
+
throw attempt.policyFailure;
|
|
4896
|
+
}
|
|
4719
4897
|
}
|
|
4720
4898
|
};
|
|
4721
4899
|
const executeV2TimedConstant = async (copies, durationNs, ramping, runSimulationInvocation, segment) => {
|
|
@@ -5257,6 +5435,7 @@ async function executeScenarioRuntime(args) {
|
|
|
5257
5435
|
})));
|
|
5258
5436
|
await invokeBeforeScenario(policies, scenario.name);
|
|
5259
5437
|
await runWarmUpAsync();
|
|
5438
|
+
reportHistoryLifecycle?.scenarioBombingStarted();
|
|
5260
5439
|
accumulator.setCurrentOperation("Bombing");
|
|
5261
5440
|
const simulations = scenario.getSimulations();
|
|
5262
5441
|
if (!simulations.length) {
|
|
@@ -5290,6 +5469,7 @@ async function executeScenarioRuntime(args) {
|
|
|
5290
5469
|
}
|
|
5291
5470
|
}
|
|
5292
5471
|
accumulator.setCurrentOperation(stopScenario || stopTestState.value ? "Stop" : "Complete");
|
|
5472
|
+
await endReportHistoryExecution();
|
|
5293
5473
|
await invokeAfterScenario(policies, scenario.name, runtime);
|
|
5294
5474
|
}
|
|
5295
5475
|
catch (error) {
|
|
@@ -5306,6 +5486,7 @@ async function executeScenarioRuntime(args) {
|
|
|
5306
5486
|
}
|
|
5307
5487
|
}
|
|
5308
5488
|
finally {
|
|
5489
|
+
await endReportHistoryExecution();
|
|
5309
5490
|
scenarioDurationsMs.set(scenario.name, Math.max(Date.now() - scenarioStartedMs, 0));
|
|
5310
5491
|
try {
|
|
5311
5492
|
await scenario.invokeClean({
|
|
@@ -5386,10 +5567,17 @@ async function waitForScenarioTasks(tasks, scenarioName, timeoutSeconds, logger,
|
|
|
5386
5567
|
throwPolicyFailure(await settled);
|
|
5387
5568
|
return;
|
|
5388
5569
|
}
|
|
5389
|
-
const
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5570
|
+
const timeoutController = new AbortController();
|
|
5571
|
+
let completed;
|
|
5572
|
+
try {
|
|
5573
|
+
completed = await Promise.race([
|
|
5574
|
+
settled.then(() => true),
|
|
5575
|
+
delayWithAbort(Math.trunc(timeoutSeconds * 1000), combineAbortSignals(signal, timeoutController.signal)).then(() => false)
|
|
5576
|
+
]);
|
|
5577
|
+
}
|
|
5578
|
+
finally {
|
|
5579
|
+
timeoutController.abort();
|
|
5580
|
+
}
|
|
5393
5581
|
if (!completed) {
|
|
5394
5582
|
if (signal.reason instanceof RuntimePolicyCallbackError) {
|
|
5395
5583
|
throw signal.reason;
|
|
@@ -7232,6 +7420,11 @@ function resolveClusterExecutionMode(options) {
|
|
|
7232
7420
|
}
|
|
7233
7421
|
return "single";
|
|
7234
7422
|
}
|
|
7423
|
+
function isDistributedReportHistoryExecution(clusterMode, options) {
|
|
7424
|
+
return clusterMode !== "single"
|
|
7425
|
+
|| options.loadEngineV2SegmentLifecycleOverride !== undefined
|
|
7426
|
+
|| Math.max(Math.trunc(options.clusterShardCount ?? 1), 1) > 1;
|
|
7427
|
+
}
|
|
7235
7428
|
function buildEmptyNodeStats(args) {
|
|
7236
7429
|
return attachNodeStatsAliases({
|
|
7237
7430
|
startedUtc: args.startedUtc,
|
|
@@ -9451,11 +9644,23 @@ function readRuntimeTrackingId(payload, selector) {
|
|
|
9451
9644
|
return null;
|
|
9452
9645
|
}
|
|
9453
9646
|
let current = body;
|
|
9454
|
-
|
|
9647
|
+
const path = selector.slice("json:".length).trim().replace(/^\$\./, "");
|
|
9648
|
+
let segments;
|
|
9649
|
+
try {
|
|
9650
|
+
segments = runtimeSafeJsonPathSegments(path);
|
|
9651
|
+
}
|
|
9652
|
+
catch {
|
|
9653
|
+
return null;
|
|
9654
|
+
}
|
|
9655
|
+
for (const segment of segments) {
|
|
9455
9656
|
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
9456
9657
|
return null;
|
|
9457
9658
|
}
|
|
9458
|
-
|
|
9659
|
+
const record = current;
|
|
9660
|
+
if (!Object.prototype.hasOwnProperty.call(record, segment)) {
|
|
9661
|
+
return null;
|
|
9662
|
+
}
|
|
9663
|
+
current = runtimeReadOwnJsonProperty(record, segment);
|
|
9459
9664
|
}
|
|
9460
9665
|
return current == null ? null : String(current);
|
|
9461
9666
|
}
|
|
@@ -9479,23 +9684,57 @@ function runtimeParseBodyAsObject(body) {
|
|
|
9479
9684
|
}
|
|
9480
9685
|
}
|
|
9481
9686
|
function setRuntimeJsonPathValue(body, path, value) {
|
|
9482
|
-
const target =
|
|
9483
|
-
|
|
9484
|
-
: {};
|
|
9485
|
-
const segments = path.split(".").filter(Boolean);
|
|
9687
|
+
const target = runtimeCloneJsonRecord(body);
|
|
9688
|
+
const segments = runtimeSafeJsonPathSegments(path);
|
|
9486
9689
|
if (!segments.length) {
|
|
9487
9690
|
return target;
|
|
9488
9691
|
}
|
|
9489
9692
|
let current = target;
|
|
9490
9693
|
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
9491
9694
|
const segment = segments[i];
|
|
9492
|
-
const next = current
|
|
9695
|
+
const next = runtimeReadOwnJsonProperty(current, segment);
|
|
9696
|
+
let child;
|
|
9493
9697
|
if (!next || typeof next !== "object" || Array.isArray(next)) {
|
|
9494
|
-
|
|
9698
|
+
child = {};
|
|
9495
9699
|
}
|
|
9496
|
-
|
|
9700
|
+
else {
|
|
9701
|
+
child = runtimeCloneJsonRecord(next);
|
|
9702
|
+
}
|
|
9703
|
+
runtimeDefineJsonProperty(current, segment, child);
|
|
9704
|
+
current = child;
|
|
9705
|
+
}
|
|
9706
|
+
runtimeDefineJsonProperty(current, segments[segments.length - 1], value);
|
|
9707
|
+
return target;
|
|
9708
|
+
}
|
|
9709
|
+
const FORBIDDEN_RUNTIME_JSON_PATH_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
|
|
9710
|
+
function runtimeSafeJsonPathSegments(path) {
|
|
9711
|
+
const segments = path.split(".").filter(Boolean);
|
|
9712
|
+
const forbidden = segments.find((segment) => FORBIDDEN_RUNTIME_JSON_PATH_SEGMENTS.has(segment));
|
|
9713
|
+
if (forbidden) {
|
|
9714
|
+
throw new Error(`Tracking selector contains forbidden JSON path segment '${forbidden}'.`);
|
|
9715
|
+
}
|
|
9716
|
+
return segments;
|
|
9717
|
+
}
|
|
9718
|
+
function runtimeDefineJsonProperty(target, key, value) {
|
|
9719
|
+
Object.defineProperty(target, key, {
|
|
9720
|
+
configurable: true,
|
|
9721
|
+
enumerable: true,
|
|
9722
|
+
value,
|
|
9723
|
+
writable: true
|
|
9724
|
+
});
|
|
9725
|
+
}
|
|
9726
|
+
function runtimeReadOwnJsonProperty(target, key) {
|
|
9727
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, key);
|
|
9728
|
+
return descriptor && "value" in descriptor ? descriptor.value : undefined;
|
|
9729
|
+
}
|
|
9730
|
+
function runtimeCloneJsonRecord(value) {
|
|
9731
|
+
const target = {};
|
|
9732
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
9733
|
+
return target;
|
|
9734
|
+
}
|
|
9735
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
9736
|
+
runtimeDefineJsonProperty(target, key, entry);
|
|
9497
9737
|
}
|
|
9498
|
-
current[segments[segments.length - 1]] = value;
|
|
9499
9738
|
return target;
|
|
9500
9739
|
}
|
|
9501
9740
|
function asTrackingRecord(value) {
|
|
@@ -9505,8 +9744,8 @@ function asTrackingRecord(value) {
|
|
|
9505
9744
|
}
|
|
9506
9745
|
function pickTrackingValue(source, ...keys) {
|
|
9507
9746
|
for (const key of keys) {
|
|
9508
|
-
if (key
|
|
9509
|
-
return source
|
|
9747
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
9748
|
+
return runtimeReadOwnJsonProperty(source, key);
|
|
9510
9749
|
}
|
|
9511
9750
|
}
|
|
9512
9751
|
return undefined;
|
|
@@ -9760,26 +9999,10 @@ function createDefaultLogger(logFilePath) {
|
|
|
9760
9999
|
function wrapLoggerWithMinimumLevel(baseLogger, minimumLogLevel) {
|
|
9761
10000
|
const threshold = logLevelOrder(minimumLogLevel);
|
|
9762
10001
|
return {
|
|
9763
|
-
debug: (message) =>
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
},
|
|
9768
|
-
info: (message) => {
|
|
9769
|
-
if (threshold <= 1) {
|
|
9770
|
-
baseLogger.info(message);
|
|
9771
|
-
}
|
|
9772
|
-
},
|
|
9773
|
-
warn: (message) => {
|
|
9774
|
-
if (threshold <= 2) {
|
|
9775
|
-
baseLogger.warn(message);
|
|
9776
|
-
}
|
|
9777
|
-
},
|
|
9778
|
-
error: (message) => {
|
|
9779
|
-
if (threshold <= 3) {
|
|
9780
|
-
baseLogger.error(message);
|
|
9781
|
-
}
|
|
9782
|
-
}
|
|
10002
|
+
debug: (message) => threshold <= 0 ? baseLogger.debug(message) : undefined,
|
|
10003
|
+
info: (message) => threshold <= 1 ? baseLogger.info(message) : undefined,
|
|
10004
|
+
warn: (message) => threshold <= 2 ? baseLogger.warn(message) : undefined,
|
|
10005
|
+
error: (message) => threshold <= 3 ? baseLogger.error(message) : undefined
|
|
9783
10006
|
};
|
|
9784
10007
|
}
|
|
9785
10008
|
function formatDefaultLoggerLine(level, message) {
|
|
@@ -11033,6 +11256,7 @@ function buildGroupedCorrelationRows(rows) {
|
|
|
11033
11256
|
LatencyMinMs: latencySamples.length ? formatOptionalLatency(latencySamples[0]) : "",
|
|
11034
11257
|
LatencyMeanMs: latencySamples.length ? formatOptionalLatency(latencySamples.reduce((sum, value) => sum + value, 0) / latencySamples.length) : "",
|
|
11035
11258
|
LatencyP50Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.5)) : "",
|
|
11259
|
+
LatencyP75Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.75)) : "",
|
|
11036
11260
|
LatencyP80Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.8)) : "",
|
|
11037
11261
|
LatencyP85Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.85)) : "",
|
|
11038
11262
|
LatencyP90Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.9)) : "",
|
|
@@ -11245,6 +11469,8 @@ exports.__loadstrikeTestExports = {
|
|
|
11245
11469
|
buildThresholdCheckExpression,
|
|
11246
11470
|
buildTrackingLeaseKey,
|
|
11247
11471
|
buildTrackingRunNamespace,
|
|
11472
|
+
boundedReportingIntervalMs,
|
|
11473
|
+
resolvedReportHistoryCadenceSeconds,
|
|
11248
11474
|
clusterNodeResultToNodeStats,
|
|
11249
11475
|
combineAbortSignals,
|
|
11250
11476
|
computeScenarioRequestCount,
|
|
@@ -11264,6 +11490,7 @@ exports.__loadstrikeTestExports = {
|
|
|
11264
11490
|
formatUtcReportTimestamp,
|
|
11265
11491
|
hasPluginRows,
|
|
11266
11492
|
inferRuntimeLegacyHttpResponseSource,
|
|
11493
|
+
isDistributedReportHistoryExecution,
|
|
11267
11494
|
isComparisonFailed,
|
|
11268
11495
|
loadJsonObject,
|
|
11269
11496
|
logLevelOrder,
|
|
@@ -11293,6 +11520,7 @@ exports.__loadstrikeTestExports = {
|
|
|
11293
11520
|
parseStrictBooleanToken,
|
|
11294
11521
|
percentile,
|
|
11295
11522
|
pickOptionalTrackingSelectorString,
|
|
11523
|
+
pickTrackingValue,
|
|
11296
11524
|
pickTrackingNumber,
|
|
11297
11525
|
produceOrConsumeTrackingPayload,
|
|
11298
11526
|
readConfiguredSinkName,
|