@loadstrike/loadstrike-sdk 1.0.31001 → 1.0.32601
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 +16 -2
- package/dist/cjs/internal/prometheus-remote-write.js +37 -0
- package/dist/cjs/internal/reporting-sink-http-error.js +17 -0
- package/dist/cjs/internal/vendor-metric-payloads.js +390 -0
- package/dist/cjs/iteration-observations.js +24 -8
- package/dist/cjs/local-report-input.js +21 -0
- package/dist/cjs/local.js +48 -63
- package/dist/cjs/report-history.js +421 -0
- package/dist/cjs/reporting-containment.js +242 -0
- package/dist/cjs/reporting-svg.js +116 -0
- package/dist/cjs/reporting.js +413 -136
- package/dist/cjs/runtime.js +237 -8
- package/dist/cjs/sinks.js +1337 -38
- package/dist/cjs/transports.js +1339 -151
- package/dist/esm/internal/prometheus-remote-write.js +31 -0
- package/dist/esm/internal/reporting-sink-http-error.js +13 -0
- package/dist/esm/internal/vendor-metric-payloads.js +382 -0
- package/dist/esm/iteration-observations.js +24 -8
- package/dist/esm/local-report-input.js +17 -0
- package/dist/esm/local.js +49 -64
- package/dist/esm/report-history.js +413 -0
- package/dist/esm/reporting-containment.js +238 -0
- package/dist/esm/reporting-svg.js +113 -0
- package/dist/esm/reporting.js +413 -136
- package/dist/esm/runtime.js +239 -10
- package/dist/esm/sinks.js +1334 -35
- package/dist/esm/transports.js +1335 -151
- package/dist/types/contracts.d.ts +1 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/internal/prometheus-remote-write.d.ts +2 -0
- package/dist/types/internal/reporting-sink-http-error.d.ts +6 -0
- package/dist/types/internal/vendor-metric-payloads.d.ts +48 -0
- package/dist/types/local-report-input.d.ts +6 -0
- package/dist/types/local.d.ts +0 -6
- package/dist/types/report-history.d.ts +124 -0
- package/dist/types/reporting-containment.d.ts +2 -0
- package/dist/types/reporting-svg.d.ts +2 -0
- package/dist/types/reporting.d.ts +6 -3
- package/dist/types/runtime.d.ts +24 -0
- package/dist/types/sinks.d.ts +134 -17
- package/dist/types/transports.d.ts +2 -0
- package/package.json +9 -3
- package/dist/cjs/internal-build.js +0 -4
- package/dist/esm/internal-build.js +0 -1
- package/dist/types/internal-build.d.ts +0 -1
package/dist/cjs/runtime.js
CHANGED
|
@@ -13,13 +13,18 @@ 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");
|
|
19
|
+
const reporting_sink_http_error_js_1 = require("./internal/reporting-sink-http-error.js");
|
|
20
|
+
const reporting_containment_js_1 = require("./reporting-containment.js");
|
|
17
21
|
const load_engine_v2_js_1 = require("./load-engine-v2.js");
|
|
18
22
|
const iteration_observations_js_1 = require("./iteration-observations.js");
|
|
19
23
|
const iteration_observation_diagnostics_js_1 = require("./iteration-observation-diagnostics.js");
|
|
20
24
|
const sink_retry_policy_js_1 = require("./sink-retry-policy.js");
|
|
21
25
|
const LOAD_ENGINE_V2_SCHEDULER_DISTRIBUTIONS = Symbol("loadstrike.load-engine-v2.scheduler-distributions");
|
|
22
26
|
const PORTAL_RUN_TOKEN_PROVIDER = Symbol.for("loadstrike.internal.portal-run-token-provider");
|
|
27
|
+
const REPORTING_INTERVAL_SECONDS = Symbol.for("loadstrike.internal.reporting-interval-seconds");
|
|
23
28
|
exports.LoadStrikeNodeType = {
|
|
24
29
|
SingleNode: "SingleNode",
|
|
25
30
|
Coordinator: "Coordinator",
|
|
@@ -291,6 +296,28 @@ class MeasurementAccumulator {
|
|
|
291
296
|
moreOrEq1200: left.moreOrEq1200 + right.moreOrEq1200
|
|
292
297
|
}, allRequestCount, durationMs);
|
|
293
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Projects only the cumulative fields used by the private HTML-report
|
|
301
|
+
* history. Legacy distributions deliberately omit latency so this cadence
|
|
302
|
+
* path never scans their retained per-request arrays.
|
|
303
|
+
*/
|
|
304
|
+
buildReportHistoryMeasurement() {
|
|
305
|
+
return projectNativeReportHistoryMeasurement(this.count, this.allBytes, this.useHistogram ? this.latencyHistogram : undefined);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Combines only bounded native latency state plus exact counters. It does
|
|
309
|
+
* not materialize status rows or any public measurement DTO.
|
|
310
|
+
*/
|
|
311
|
+
buildCombinedReportHistoryMeasurement(other) {
|
|
312
|
+
const count = this.count + other.count;
|
|
313
|
+
const bytes = this.allBytes + other.allBytes;
|
|
314
|
+
if (!this.useHistogram || !other.useHistogram) {
|
|
315
|
+
return projectNativeReportHistoryMeasurement(count, bytes);
|
|
316
|
+
}
|
|
317
|
+
const latency = this.latencyHistogram.clone();
|
|
318
|
+
latency.merge(other.latencyHistogram);
|
|
319
|
+
return projectNativeReportHistoryMeasurement(count, bytes, latency);
|
|
320
|
+
}
|
|
294
321
|
histogramSnapshot() {
|
|
295
322
|
return {
|
|
296
323
|
count: this.count,
|
|
@@ -304,6 +331,24 @@ class MeasurementAccumulator {
|
|
|
304
331
|
};
|
|
305
332
|
}
|
|
306
333
|
}
|
|
334
|
+
function projectNativeReportHistoryMeasurement(count, bytes, latency) {
|
|
335
|
+
const measurement = {
|
|
336
|
+
count,
|
|
337
|
+
bytes,
|
|
338
|
+
approximate: latency?.maxRelativeError !== undefined
|
|
339
|
+
&& latency.maxRelativeError > 0
|
|
340
|
+
};
|
|
341
|
+
if (!latency || latency.count === 0n) {
|
|
342
|
+
return measurement;
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
...measurement,
|
|
346
|
+
percent50Ms: Number(latency.percentile(0.5)) / 1000,
|
|
347
|
+
percent75Ms: Number(latency.percentile(0.75)) / 1000,
|
|
348
|
+
percent95Ms: Number(latency.percentile(0.95)) / 1000,
|
|
349
|
+
percent99Ms: Number(latency.percentile(0.99)) / 1000
|
|
350
|
+
};
|
|
351
|
+
}
|
|
307
352
|
function buildHistogramMeasurement(snapshot, allRequestCount, durationMs) {
|
|
308
353
|
const count = snapshot.count;
|
|
309
354
|
const totalDurationMs = Math.max(durationMs, 0);
|
|
@@ -642,6 +687,21 @@ class ScenarioStatsAccumulator {
|
|
|
642
687
|
step.record(reply, observedLatencyMs);
|
|
643
688
|
return step.sortIndex;
|
|
644
689
|
}
|
|
690
|
+
/**
|
|
691
|
+
* Captures the bounded, private history view without building scenario
|
|
692
|
+
* steps, status rows, plugins, aliases, or any other public result shape.
|
|
693
|
+
*/
|
|
694
|
+
buildReportHistorySnapshot() {
|
|
695
|
+
const ok = this.ok.buildReportHistoryMeasurement();
|
|
696
|
+
const failed = this.fail.buildReportHistoryMeasurement();
|
|
697
|
+
return {
|
|
698
|
+
scenarioName: this.scenarioName,
|
|
699
|
+
sortIndex: this.sortIndex,
|
|
700
|
+
all: this.ok.buildCombinedReportHistoryMeasurement(this.fail),
|
|
701
|
+
...(ok.count > 0 ? { ok } : {}),
|
|
702
|
+
...(failed.count > 0 ? { failed } : {})
|
|
703
|
+
};
|
|
704
|
+
}
|
|
645
705
|
/**
|
|
646
706
|
* Builds the configured payload or helper object.
|
|
647
707
|
* Use this when all builder inputs are ready to be materialized.
|
|
@@ -690,6 +750,15 @@ class ScenarioStatsAccumulator {
|
|
|
690
750
|
return attachScenarioStatsAliases(scenario);
|
|
691
751
|
}
|
|
692
752
|
}
|
|
753
|
+
function isRetryableReportingSinkError(sink, error) {
|
|
754
|
+
if (!(sink instanceof sinks_js_1.PrometheusRemoteWriteReportingSink)) {
|
|
755
|
+
return true;
|
|
756
|
+
}
|
|
757
|
+
if (!(error instanceof reporting_sink_http_error_js_1.ReportingSinkHttpError)) {
|
|
758
|
+
return true;
|
|
759
|
+
}
|
|
760
|
+
return error.status === 429 || error.status < 400 || error.status >= 500;
|
|
761
|
+
}
|
|
693
762
|
class LoadStrikeResponse {
|
|
694
763
|
/**
|
|
695
764
|
* Creates a successful reply.
|
|
@@ -3438,6 +3507,8 @@ class LoadStrikeRunner {
|
|
|
3438
3507
|
return this.buildContext();
|
|
3439
3508
|
}
|
|
3440
3509
|
async run(args = []) {
|
|
3510
|
+
(0, reporting_containment_js_1.assertNoUnsupportedReportingSinkGraph)(this.options.reportingSinks ?? []);
|
|
3511
|
+
assertNoUnsupportedNativeTrackingScenarios(this.scenarios);
|
|
3441
3512
|
if (this.contextConfigurators.length) {
|
|
3442
3513
|
return new LoadStrikeRunner(this.scenarios, this.buildContext().toRunnerOptions(), [], this.internalOptions).run(args);
|
|
3443
3514
|
}
|
|
@@ -3449,7 +3520,7 @@ class LoadStrikeRunner {
|
|
|
3449
3520
|
const scenarioAccumulators = new Map();
|
|
3450
3521
|
const scenarioDurationsMs = new Map();
|
|
3451
3522
|
const stepStats = new Map();
|
|
3452
|
-
const sinks = (this.options.reportingSinks ?? []).map((sink) => (0, sinks_js_1.cloneReportingSinkForRun)(sink));
|
|
3523
|
+
const sinks = (this.options.reportingSinks ?? []).map((sink) => (0, sinks_js_1.cloneReportingSinkForRun)(sink, this.options.infraConfig ?? {}));
|
|
3453
3524
|
const sinkStates = sinks.map((sink, index) => ({
|
|
3454
3525
|
sink,
|
|
3455
3526
|
disabled: false,
|
|
@@ -3509,6 +3580,58 @@ class LoadStrikeRunner {
|
|
|
3509
3580
|
licenseSession = await licenseClient.acquireLicenseLease(licensePayload);
|
|
3510
3581
|
const loggerSetup = createLoggerSetup(this.options.loggerConfig, this.options.minimumLogLevel, this.options, testInfo, nodeInfo);
|
|
3511
3582
|
const runLogger = loggerSetup.logger;
|
|
3583
|
+
const htmlReportHistoryRequested = (this.options.reportsEnabled ?? true)
|
|
3584
|
+
&& normalizeReportFormats(this.options.reportFormats ?? ["html", "txt", "csv", "md"]).includes("html");
|
|
3585
|
+
const distributedReportHistory = isDistributedReportHistoryExecution(clusterMode, this.options);
|
|
3586
|
+
let localReportInput = htmlReportHistoryRequested
|
|
3587
|
+
&& distributedReportHistory
|
|
3588
|
+
? (0, local_report_input_js_1.distributedLocalReportInput)()
|
|
3589
|
+
: (0, local_report_input_js_1.emptyLocalReportInput)();
|
|
3590
|
+
let reportHistoryCollector;
|
|
3591
|
+
let reportHistoryLifecycle;
|
|
3592
|
+
if (htmlReportHistoryRequested && !distributedReportHistory) {
|
|
3593
|
+
const warnReportHistoryFailure = (category, error) => {
|
|
3594
|
+
const exceptionClasses = error === undefined
|
|
3595
|
+
? ""
|
|
3596
|
+
: `; exception_classes=${(0, report_history_js_1.sanitizedExceptionClassChain)(error)}`;
|
|
3597
|
+
try {
|
|
3598
|
+
runLogger.warn(`LoadStrike report history unavailable: ${category}${exceptionClasses}.`);
|
|
3599
|
+
}
|
|
3600
|
+
catch {
|
|
3601
|
+
// Report-only diagnostics are best-effort.
|
|
3602
|
+
}
|
|
3603
|
+
};
|
|
3604
|
+
reportHistoryCollector = new report_history_js_1.ReportHistoryCollector({
|
|
3605
|
+
scenarioCount: selectedScenarios.length,
|
|
3606
|
+
onCaptureError: (error) => warnReportHistoryFailure("capture_failure", error)
|
|
3607
|
+
});
|
|
3608
|
+
if (!reportHistoryCollector.available) {
|
|
3609
|
+
warnReportHistoryFailure("budget_pressure");
|
|
3610
|
+
}
|
|
3611
|
+
const snapshotFactory = () => Array.from(scenarioAccumulators.values())
|
|
3612
|
+
.map((value) => value.buildReportHistorySnapshot())
|
|
3613
|
+
.sort((left, right) => (left.sortIndex ?? 0) - (right.sortIndex ?? 0)
|
|
3614
|
+
|| left.scenarioName.localeCompare(right.scenarioName));
|
|
3615
|
+
const reportHistoryWorker = new report_history_js_1.ReportHistoryWorker({
|
|
3616
|
+
collector: reportHistoryCollector,
|
|
3617
|
+
cadenceSeconds: resolvedReportHistoryCadenceSeconds(this.options.reportingIntervalSeconds ?? 5),
|
|
3618
|
+
snapshotFactory,
|
|
3619
|
+
onFailure: (category, exceptionClasses) => {
|
|
3620
|
+
try {
|
|
3621
|
+
runLogger.warn(`LoadStrike report history unavailable: ${category}; exception_classes=${exceptionClasses}.`);
|
|
3622
|
+
}
|
|
3623
|
+
catch {
|
|
3624
|
+
// Report-only diagnostics are best-effort.
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
});
|
|
3628
|
+
reportHistoryLifecycle = new report_history_js_1.ReportHistoryLifecycleCoordinator({
|
|
3629
|
+
scenarioCount: selectedScenarios.length,
|
|
3630
|
+
start: () => reportHistoryWorker.start(),
|
|
3631
|
+
stopAndFinalize: () => reportHistoryWorker.stopAndFinalize(),
|
|
3632
|
+
stopWithoutFinalizing: () => reportHistoryWorker.stopWithoutFinalizing()
|
|
3633
|
+
});
|
|
3634
|
+
}
|
|
3512
3635
|
const scenarioStartInfos = selectedScenarios.map((scenario, index) => {
|
|
3513
3636
|
const startInfo = {
|
|
3514
3637
|
scenarioName: scenario.name,
|
|
@@ -3522,6 +3645,12 @@ class LoadStrikeRunner {
|
|
|
3522
3645
|
testInfo,
|
|
3523
3646
|
getNodeInfo: () => attachNodeInfoAliases({ ...nodeInfo })
|
|
3524
3647
|
};
|
|
3648
|
+
Object.defineProperty(baseContext, REPORTING_INTERVAL_SECONDS, {
|
|
3649
|
+
value: this.options.reportingIntervalSeconds ?? 5,
|
|
3650
|
+
enumerable: false,
|
|
3651
|
+
configurable: false,
|
|
3652
|
+
writable: false
|
|
3653
|
+
});
|
|
3525
3654
|
attachBaseContextAliases(baseContext);
|
|
3526
3655
|
const sinkSession = {
|
|
3527
3656
|
startedUtc: createdUtc,
|
|
@@ -3576,6 +3705,7 @@ class LoadStrikeRunner {
|
|
|
3576
3705
|
name: state.name,
|
|
3577
3706
|
iterationObservationPortalSink: Boolean(state.sink.iterationObservationPortalSink),
|
|
3578
3707
|
iterationObservationShapeLimited: Boolean(state.sink.iterationObservationShapeLimited),
|
|
3708
|
+
retryableErrorClassifier: (error) => isRetryableReportingSinkError(state.sink, error),
|
|
3579
3709
|
saveIterationBatch: resolveSinkSaveIterationBatch(state.sink),
|
|
3580
3710
|
completeIterationObservationStream: resolveSinkCompleteIterationObservationStream(state.sink)
|
|
3581
3711
|
}));
|
|
@@ -3618,7 +3748,7 @@ class LoadStrikeRunner {
|
|
|
3618
3748
|
realtimeInFlight = false;
|
|
3619
3749
|
}
|
|
3620
3750
|
};
|
|
3621
|
-
const reportingIntervalMs =
|
|
3751
|
+
const reportingIntervalMs = boundedReportingIntervalMs(this.options.reportingIntervalSeconds ?? 5);
|
|
3622
3752
|
const triggerRealtimeSnapshot = () => {
|
|
3623
3753
|
if (realtimeCurrent) {
|
|
3624
3754
|
return;
|
|
@@ -3705,6 +3835,7 @@ class LoadStrikeRunner {
|
|
|
3705
3835
|
iterationObservationRunId,
|
|
3706
3836
|
iterationObservationResultOwnerId,
|
|
3707
3837
|
iterationObservationProcessGroup,
|
|
3838
|
+
reportHistoryLifecycle,
|
|
3708
3839
|
executeScenarioInvocation: (targetScenario, context, operation) => this.executeScenarioInvocation(targetScenario, context, operation),
|
|
3709
3840
|
invokeBeforeScenario: (runtimePolicies, scenarioName) => this.invokeBeforeScenario(runtimePolicies, scenarioName, policyErrors, runtimePolicyErrorMode),
|
|
3710
3841
|
invokeAfterScenario: (runtimePolicies, scenarioName, stats) => this.invokeAfterScenario(runtimePolicies, scenarioName, stats, policyErrors, runtimePolicyErrorMode),
|
|
@@ -3792,9 +3923,14 @@ class LoadStrikeRunner {
|
|
|
3792
3923
|
...iterationObservationReporter.buildWarnings()
|
|
3793
3924
|
];
|
|
3794
3925
|
result.pluginsData = mergePluginData(result.pluginsData, await this.collectPluginData(plugins, attachRunResultAliases(result), pluginLifecycleErrors));
|
|
3926
|
+
if (reportHistoryCollector) {
|
|
3927
|
+
localReportInput = {
|
|
3928
|
+
history: reportHistoryCollector.toProjection()
|
|
3929
|
+
};
|
|
3930
|
+
}
|
|
3795
3931
|
const finalizedResult = attachRunResultAliases(result);
|
|
3796
3932
|
finalizedResult.logFiles = mergeStringArrays(finalizedResult.logFiles, loggerSetup.logFiles);
|
|
3797
|
-
finalizedResult.reportFiles = this.writeReports(finalizedResult);
|
|
3933
|
+
finalizedResult.reportFiles = this.writeReports(finalizedResult, localReportInput);
|
|
3798
3934
|
finalizedResult.disabledSinks = sinkStates.filter((x) => x.disabled).map((x) => x.name);
|
|
3799
3935
|
await this.emitRunResult(sinkStates, finalizedResult, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3800
3936
|
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
@@ -3805,6 +3941,7 @@ class LoadStrikeRunner {
|
|
|
3805
3941
|
return finalizedResult;
|
|
3806
3942
|
}
|
|
3807
3943
|
finally {
|
|
3944
|
+
reportHistoryLifecycle?.stopWithoutFinalizing();
|
|
3808
3945
|
await stopRealtimeReporting();
|
|
3809
3946
|
if (!iterationObservationsFinalized) {
|
|
3810
3947
|
await iterationObservationReporter.sealAndDrain().catch(() => { });
|
|
@@ -4203,7 +4340,9 @@ class LoadStrikeRunner {
|
|
|
4203
4340
|
return;
|
|
4204
4341
|
}
|
|
4205
4342
|
catch (error) {
|
|
4206
|
-
const
|
|
4343
|
+
const retryable = isRetryableReportingSinkError(state.sink, error);
|
|
4344
|
+
const exhausted = attempts >= maximumAttempts || !retryable;
|
|
4345
|
+
const reportedMaximumAttempts = retryable ? maximumAttempts : attempts;
|
|
4207
4346
|
const nextDelayMs = exhausted ? 0 : (0, sink_retry_policy_js_1.sinkRetryDelayMs)(backoffMs, attempts);
|
|
4208
4347
|
(0, iteration_observation_diagnostics_js_1.logIterationObservationFailure)(logger, exhausted ? "error" : "warn", {
|
|
4209
4348
|
sinkName: state.name,
|
|
@@ -4213,7 +4352,7 @@ class LoadStrikeRunner {
|
|
|
4213
4352
|
resultOwnerId: "",
|
|
4214
4353
|
observationCount: 0,
|
|
4215
4354
|
attempt: attempts,
|
|
4216
|
-
maximumAttempts,
|
|
4355
|
+
maximumAttempts: reportedMaximumAttempts,
|
|
4217
4356
|
nextDelayMs
|
|
4218
4357
|
}, error);
|
|
4219
4358
|
if (exhausted) {
|
|
@@ -4282,7 +4421,7 @@ class LoadStrikeRunner {
|
|
|
4282
4421
|
message: String(error ?? "runtime policy callback failed")
|
|
4283
4422
|
});
|
|
4284
4423
|
}
|
|
4285
|
-
writeReports(result) {
|
|
4424
|
+
writeReports(result, localReportInput = (0, local_report_input_js_1.emptyLocalReportInput)()) {
|
|
4286
4425
|
const reportsEnabled = this.options.reportsEnabled ?? true;
|
|
4287
4426
|
if (!reportsEnabled) {
|
|
4288
4427
|
return [];
|
|
@@ -4307,7 +4446,7 @@ class LoadStrikeRunner {
|
|
|
4307
4446
|
(0, node_fs_1.writeFileSync)(path, (0, reporting_js_1.buildDotnetMarkdownReport)(nodeStats), "utf8");
|
|
4308
4447
|
}
|
|
4309
4448
|
else if (format === "html") {
|
|
4310
|
-
(0, node_fs_1.writeFileSync)(path, (0, reporting_js_1.buildDotnetHtmlReport)(nodeStats), "utf8");
|
|
4449
|
+
(0, node_fs_1.writeFileSync)(path, (0, reporting_js_1.buildDotnetHtmlReport)(nodeStats, localReportInput), "utf8");
|
|
4311
4450
|
}
|
|
4312
4451
|
written.push(path);
|
|
4313
4452
|
}
|
|
@@ -4341,6 +4480,19 @@ function hasPluginRows(value) {
|
|
|
4341
4480
|
}
|
|
4342
4481
|
return value.tables.some((table) => Array.isArray(table.rows) && table.rows.length > 0);
|
|
4343
4482
|
}
|
|
4483
|
+
function boundedReportingIntervalMs(seconds) {
|
|
4484
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
4485
|
+
return 1;
|
|
4486
|
+
}
|
|
4487
|
+
const milliseconds = seconds * 1000;
|
|
4488
|
+
if (!Number.isFinite(milliseconds) || milliseconds >= 2147483647) {
|
|
4489
|
+
return 2147483647;
|
|
4490
|
+
}
|
|
4491
|
+
return Math.max(Math.trunc(milliseconds), 1);
|
|
4492
|
+
}
|
|
4493
|
+
function resolvedReportHistoryCadenceSeconds(seconds) {
|
|
4494
|
+
return boundedReportingIntervalMs(seconds) / 1000;
|
|
4495
|
+
}
|
|
4344
4496
|
async function executeV2FixedArrivals(args) {
|
|
4345
4497
|
const { rate, intervalNs, totalArrivals, budget, cancellationToken, shouldStopNow, nextInstanceInfo, runBombingInvocation, logger, scenarioName, deadlineOffsetsNs, tolerancesNs, ownedOrdinals, shardIndex = 0, shardCount = 1, telemetry, segment } = args;
|
|
4346
4498
|
const segmentStartNs = process.hrtime.bigint();
|
|
@@ -4477,7 +4629,15 @@ function maxBigInt(left, right) {
|
|
|
4477
4629
|
return left > right ? left : right;
|
|
4478
4630
|
}
|
|
4479
4631
|
async function executeScenarioRuntime(args) {
|
|
4480
|
-
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;
|
|
4632
|
+
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;
|
|
4633
|
+
let reportHistoryExecutionEnded = false;
|
|
4634
|
+
const endReportHistoryExecution = async () => {
|
|
4635
|
+
if (reportHistoryExecutionEnded) {
|
|
4636
|
+
return;
|
|
4637
|
+
}
|
|
4638
|
+
reportHistoryExecutionEnded = true;
|
|
4639
|
+
await reportHistoryLifecycle?.scenarioExecutionEnded();
|
|
4640
|
+
};
|
|
4481
4641
|
const scenarioStartedMs = Date.now();
|
|
4482
4642
|
const scenarioContextData = {};
|
|
4483
4643
|
const registeredMetrics = [];
|
|
@@ -5298,6 +5458,7 @@ async function executeScenarioRuntime(args) {
|
|
|
5298
5458
|
})));
|
|
5299
5459
|
await invokeBeforeScenario(policies, scenario.name);
|
|
5300
5460
|
await runWarmUpAsync();
|
|
5461
|
+
reportHistoryLifecycle?.scenarioBombingStarted();
|
|
5301
5462
|
accumulator.setCurrentOperation("Bombing");
|
|
5302
5463
|
const simulations = scenario.getSimulations();
|
|
5303
5464
|
if (!simulations.length) {
|
|
@@ -5331,6 +5492,7 @@ async function executeScenarioRuntime(args) {
|
|
|
5331
5492
|
}
|
|
5332
5493
|
}
|
|
5333
5494
|
accumulator.setCurrentOperation(stopScenario || stopTestState.value ? "Stop" : "Complete");
|
|
5495
|
+
await endReportHistoryExecution();
|
|
5334
5496
|
await invokeAfterScenario(policies, scenario.name, runtime);
|
|
5335
5497
|
}
|
|
5336
5498
|
catch (error) {
|
|
@@ -5347,6 +5509,7 @@ async function executeScenarioRuntime(args) {
|
|
|
5347
5509
|
}
|
|
5348
5510
|
}
|
|
5349
5511
|
finally {
|
|
5512
|
+
await endReportHistoryExecution();
|
|
5350
5513
|
scenarioDurationsMs.set(scenario.name, Math.max(Date.now() - scenarioStartedMs, 0));
|
|
5351
5514
|
try {
|
|
5352
5515
|
await scenario.invokeClean({
|
|
@@ -7280,6 +7443,11 @@ function resolveClusterExecutionMode(options) {
|
|
|
7280
7443
|
}
|
|
7281
7444
|
return "single";
|
|
7282
7445
|
}
|
|
7446
|
+
function isDistributedReportHistoryExecution(clusterMode, options) {
|
|
7447
|
+
return clusterMode !== "single"
|
|
7448
|
+
|| options.loadEngineV2SegmentLifecycleOverride !== undefined
|
|
7449
|
+
|| Math.max(Math.trunc(options.clusterShardCount ?? 1), 1) > 1;
|
|
7450
|
+
}
|
|
7283
7451
|
function buildEmptyNodeStats(args) {
|
|
7284
7452
|
return attachNodeStatsAliases({
|
|
7285
7453
|
startedUtc: args.startedUtc,
|
|
@@ -8751,6 +8919,7 @@ class ManagedScenarioTrackingRuntime {
|
|
|
8751
8919
|
}
|
|
8752
8920
|
async dispose() {
|
|
8753
8921
|
this.shutdown = true;
|
|
8922
|
+
this.interruptEndpointAdapters();
|
|
8754
8923
|
this.rejectOutstandingWaiters();
|
|
8755
8924
|
await Promise.all([
|
|
8756
8925
|
this.sourceLoop,
|
|
@@ -8816,6 +8985,7 @@ class ManagedScenarioTrackingRuntime {
|
|
|
8816
8985
|
failureSeen = failureSeen || this.isFailedObservationOutcome(outcome);
|
|
8817
8986
|
}
|
|
8818
8987
|
this.shutdown = true;
|
|
8988
|
+
this.interruptEndpointAdapters();
|
|
8819
8989
|
await Promise.all([
|
|
8820
8990
|
this.sourceLoop,
|
|
8821
8991
|
this.destinationLoop,
|
|
@@ -8832,6 +9002,20 @@ class ManagedScenarioTrackingRuntime {
|
|
|
8832
9002
|
? LoadStrikeResponse.fail("tracking_failures", "One or more observed source or destination events did not correlate successfully.", 0)
|
|
8833
9003
|
: LoadStrikeResponse.ok("observed");
|
|
8834
9004
|
}
|
|
9005
|
+
interruptEndpointAdapters() {
|
|
9006
|
+
try {
|
|
9007
|
+
this.sourceAdapter.interrupt?.();
|
|
9008
|
+
}
|
|
9009
|
+
catch {
|
|
9010
|
+
// Shutdown remains best-effort and still disposes every adapter below.
|
|
9011
|
+
}
|
|
9012
|
+
try {
|
|
9013
|
+
this.destinationAdapter?.interrupt?.();
|
|
9014
|
+
}
|
|
9015
|
+
catch {
|
|
9016
|
+
// Shutdown remains best-effort and still disposes every adapter below.
|
|
9017
|
+
}
|
|
9018
|
+
}
|
|
8835
9019
|
async consumeSourceLoop() {
|
|
8836
9020
|
while (!this.shutdown) {
|
|
8837
9021
|
try {
|
|
@@ -9423,6 +9607,20 @@ function mapRuntimeTrackingEndpointSpec(spec, useLoadStrikeTraceIdHeader = false
|
|
|
9423
9607
|
azureEventHubs: asTrackingRecord(pickTrackingValue(spec, "AzureEventHubs", "azureEventHubs")),
|
|
9424
9608
|
sqs: asTrackingRecord(pickTrackingValue(spec, "Sqs", "sqs")),
|
|
9425
9609
|
pushDiffusion: asTrackingRecord(pickTrackingValue(spec, "PushDiffusion", "pushDiffusion")),
|
|
9610
|
+
grpc: mapRuntimeEndpointProtocolOptions(spec, ["Grpc", "grpc"], [
|
|
9611
|
+
"Target", "target", "ServiceName", "serviceName", "MethodName", "methodName",
|
|
9612
|
+
"MethodType", "methodType", "Deadline", "deadline", "DeadlineSeconds", "deadlineSeconds",
|
|
9613
|
+
"DeadlineMs", "deadlineMs", "Metadata", "metadata", "Produce", "produce", "Consume", "consume",
|
|
9614
|
+
"ProduceAsync", "produceAsync", "ConsumeAsync", "consumeAsync", "ConnectionMetadata", "connectionMetadata",
|
|
9615
|
+
"NativeClient", "nativeClient"
|
|
9616
|
+
]),
|
|
9617
|
+
webSocket: mapRuntimeEndpointProtocolOptions(spec, ["WebSocket", "webSocket"], [
|
|
9618
|
+
"Url", "url", "Subprotocols", "subprotocols", "ConnectTimeout", "connectTimeout",
|
|
9619
|
+
"ConnectTimeoutSeconds", "connectTimeoutSeconds", "ConnectTimeoutMs", "connectTimeoutMs",
|
|
9620
|
+
"CloseTimeout", "closeTimeout", "CloseTimeoutSeconds", "closeTimeoutSeconds", "CloseTimeoutMs", "closeTimeoutMs",
|
|
9621
|
+
"Produce", "produce", "Consume", "consume", "ProduceAsync", "produceAsync", "ConsumeAsync", "consumeAsync",
|
|
9622
|
+
"ConnectionMetadata", "connectionMetadata", "NativeClient", "nativeClient"
|
|
9623
|
+
]),
|
|
9426
9624
|
delegate: typeof delegateProduce === "function"
|
|
9427
9625
|
|| typeof delegateConsume === "function"
|
|
9428
9626
|
|| typeof delegateProduceAsync === "function"
|
|
@@ -9445,6 +9643,19 @@ function mapRuntimeTrackingEndpointSpec(spec, useLoadStrikeTraceIdHeader = false
|
|
|
9445
9643
|
: undefined
|
|
9446
9644
|
};
|
|
9447
9645
|
}
|
|
9646
|
+
function mapRuntimeEndpointProtocolOptions(spec, nestedKeys, flatKeys) {
|
|
9647
|
+
const nested = asTrackingRecord(pickTrackingValue(spec, ...nestedKeys));
|
|
9648
|
+
if (Object.keys(nested).length > 0) {
|
|
9649
|
+
return nested;
|
|
9650
|
+
}
|
|
9651
|
+
const options = {};
|
|
9652
|
+
for (const key of flatKeys) {
|
|
9653
|
+
if (Object.prototype.hasOwnProperty.call(spec, key)) {
|
|
9654
|
+
options[key] = pickTrackingValue(spec, key);
|
|
9655
|
+
}
|
|
9656
|
+
}
|
|
9657
|
+
return options;
|
|
9658
|
+
}
|
|
9448
9659
|
function normalizeRuntimeTrackingPayload(payload, endpoint, index) {
|
|
9449
9660
|
const normalized = {
|
|
9450
9661
|
headers: {
|
|
@@ -10208,6 +10419,20 @@ function assertNoDisableLicenseEnforcementOption(value, source) {
|
|
|
10208
10419
|
}
|
|
10209
10420
|
}
|
|
10210
10421
|
}
|
|
10422
|
+
function assertNoUnsupportedNativeTrackingScenarios(scenarios) {
|
|
10423
|
+
for (const scenario of scenarios) {
|
|
10424
|
+
const tracking = scenario.getTrackingConfiguration();
|
|
10425
|
+
if (!tracking) {
|
|
10426
|
+
continue;
|
|
10427
|
+
}
|
|
10428
|
+
for (const field of ["Source", "Destination"]) {
|
|
10429
|
+
const endpoint = asTrackingRecord(pickTrackingValue(tracking, field, field.toLowerCase()));
|
|
10430
|
+
if (Object.keys(endpoint).length > 0) {
|
|
10431
|
+
(0, transports_js_1.validateNativeEndpointExecutionSupport)(endpoint);
|
|
10432
|
+
}
|
|
10433
|
+
}
|
|
10434
|
+
}
|
|
10435
|
+
}
|
|
10211
10436
|
function normalizeRunContextCollectionShapes(values) {
|
|
10212
10437
|
assertNoDisableLicenseEnforcementOption(values, "LoadStrikeContext");
|
|
10213
10438
|
const normalized = {
|
|
@@ -11111,6 +11336,7 @@ function buildGroupedCorrelationRows(rows) {
|
|
|
11111
11336
|
LatencyMinMs: latencySamples.length ? formatOptionalLatency(latencySamples[0]) : "",
|
|
11112
11337
|
LatencyMeanMs: latencySamples.length ? formatOptionalLatency(latencySamples.reduce((sum, value) => sum + value, 0) / latencySamples.length) : "",
|
|
11113
11338
|
LatencyP50Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.5)) : "",
|
|
11339
|
+
LatencyP75Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.75)) : "",
|
|
11114
11340
|
LatencyP80Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.8)) : "",
|
|
11115
11341
|
LatencyP85Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.85)) : "",
|
|
11116
11342
|
LatencyP90Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.9)) : "",
|
|
@@ -11323,6 +11549,8 @@ exports.__loadstrikeTestExports = {
|
|
|
11323
11549
|
buildThresholdCheckExpression,
|
|
11324
11550
|
buildTrackingLeaseKey,
|
|
11325
11551
|
buildTrackingRunNamespace,
|
|
11552
|
+
boundedReportingIntervalMs,
|
|
11553
|
+
resolvedReportHistoryCadenceSeconds,
|
|
11326
11554
|
clusterNodeResultToNodeStats,
|
|
11327
11555
|
combineAbortSignals,
|
|
11328
11556
|
computeScenarioRequestCount,
|
|
@@ -11342,6 +11570,7 @@ exports.__loadstrikeTestExports = {
|
|
|
11342
11570
|
formatUtcReportTimestamp,
|
|
11343
11571
|
hasPluginRows,
|
|
11344
11572
|
inferRuntimeLegacyHttpResponseSource,
|
|
11573
|
+
isDistributedReportHistoryExecution,
|
|
11345
11574
|
isComparisonFailed,
|
|
11346
11575
|
loadJsonObject,
|
|
11347
11576
|
logLevelOrder,
|