@loadstrike/loadstrike-sdk 1.0.31001 → 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 +2 -0
- package/dist/cjs/local-report-input.js +21 -0
- package/dist/cjs/report-history.js +421 -0
- package/dist/cjs/reporting-svg.js +116 -0
- package/dist/cjs/reporting.js +413 -136
- package/dist/cjs/runtime.js +154 -5
- package/dist/esm/local-report-input.js +17 -0
- package/dist/esm/report-history.js +413 -0
- package/dist/esm/reporting-svg.js +113 -0
- package/dist/esm/reporting.js +413 -136
- package/dist/esm/runtime.js +154 -5
- 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 +23 -0
- package/package.json +1 -1
package/dist/esm/runtime.js
CHANGED
|
@@ -7,6 +7,8 @@ import { DistributedClusterAgent, DistributedClusterCoordinator, buildLoadEngine
|
|
|
7
7
|
import { CorrelationStoreConfiguration, CrossPlatformTrackingRuntime, RedisCorrelationStore, RedisCorrelationStoreOptions, TrackingFieldSelector } from "./correlation.js";
|
|
8
8
|
import { EndpointAdapterFactory, LOADSTRIKE_TRACE_ID_TRACKING_FIELD } from "./transports.js";
|
|
9
9
|
import { buildDotnetCsvReport, buildDotnetHtmlReport, buildDotnetMarkdownReport, buildDotnetTxtReport } from "./reporting.js";
|
|
10
|
+
import { ReportHistoryCollector, ReportHistoryLifecycleCoordinator, ReportHistoryWorker, sanitizedExceptionClassChain } from "./report-history.js";
|
|
11
|
+
import { distributedLocalReportInput, emptyLocalReportInput } from "./local-report-input.js";
|
|
10
12
|
import { PortalReportingSink, cloneReportingSinkForRun } from "./sinks.js";
|
|
11
13
|
import { LoadEngineV2ExecutionBudget, buildLoadEngineV2TrafficMixSeedId, LoadStrikeHistogramV1, parseLoadEngineV2HistogramArtifact, serializeLoadEngineV2HistogramArtifact, classifyLoadEngineV2Arrival, loadEngineV2FixedArrivalCount, loadEngineV2FixedDeadlineNs, loadEngineV2LatenessToleranceNs, loadEngineV2TrafficMixLaneUnitCount, loadEngineV2TrafficMixOwnedUnits, planRampingInjectionDeadlines, planRampingConstantDeadlines, planRandomInjectionDeadlines, fnv1a32 } from "./load-engine-v2.js";
|
|
12
14
|
import { DEFAULT_ITERATION_OBSERVATION_SETTINGS, IterationObservationReporter, createIterationObservation, createIterationStepObservation, utcNowNs, validateIterationObservationSettings } from "./iteration-observations.js";
|
|
@@ -283,6 +285,28 @@ class MeasurementAccumulator {
|
|
|
283
285
|
moreOrEq1200: left.moreOrEq1200 + right.moreOrEq1200
|
|
284
286
|
}, allRequestCount, durationMs);
|
|
285
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Projects only the cumulative fields used by the private HTML-report
|
|
290
|
+
* history. Legacy distributions deliberately omit latency so this cadence
|
|
291
|
+
* path never scans their retained per-request arrays.
|
|
292
|
+
*/
|
|
293
|
+
buildReportHistoryMeasurement() {
|
|
294
|
+
return projectNativeReportHistoryMeasurement(this.count, this.allBytes, this.useHistogram ? this.latencyHistogram : undefined);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Combines only bounded native latency state plus exact counters. It does
|
|
298
|
+
* not materialize status rows or any public measurement DTO.
|
|
299
|
+
*/
|
|
300
|
+
buildCombinedReportHistoryMeasurement(other) {
|
|
301
|
+
const count = this.count + other.count;
|
|
302
|
+
const bytes = this.allBytes + other.allBytes;
|
|
303
|
+
if (!this.useHistogram || !other.useHistogram) {
|
|
304
|
+
return projectNativeReportHistoryMeasurement(count, bytes);
|
|
305
|
+
}
|
|
306
|
+
const latency = this.latencyHistogram.clone();
|
|
307
|
+
latency.merge(other.latencyHistogram);
|
|
308
|
+
return projectNativeReportHistoryMeasurement(count, bytes, latency);
|
|
309
|
+
}
|
|
286
310
|
histogramSnapshot() {
|
|
287
311
|
return {
|
|
288
312
|
count: this.count,
|
|
@@ -296,6 +320,24 @@ class MeasurementAccumulator {
|
|
|
296
320
|
};
|
|
297
321
|
}
|
|
298
322
|
}
|
|
323
|
+
function projectNativeReportHistoryMeasurement(count, bytes, latency) {
|
|
324
|
+
const measurement = {
|
|
325
|
+
count,
|
|
326
|
+
bytes,
|
|
327
|
+
approximate: latency?.maxRelativeError !== undefined
|
|
328
|
+
&& latency.maxRelativeError > 0
|
|
329
|
+
};
|
|
330
|
+
if (!latency || latency.count === 0n) {
|
|
331
|
+
return measurement;
|
|
332
|
+
}
|
|
333
|
+
return {
|
|
334
|
+
...measurement,
|
|
335
|
+
percent50Ms: Number(latency.percentile(0.5)) / 1000,
|
|
336
|
+
percent75Ms: Number(latency.percentile(0.75)) / 1000,
|
|
337
|
+
percent95Ms: Number(latency.percentile(0.95)) / 1000,
|
|
338
|
+
percent99Ms: Number(latency.percentile(0.99)) / 1000
|
|
339
|
+
};
|
|
340
|
+
}
|
|
299
341
|
function buildHistogramMeasurement(snapshot, allRequestCount, durationMs) {
|
|
300
342
|
const count = snapshot.count;
|
|
301
343
|
const totalDurationMs = Math.max(durationMs, 0);
|
|
@@ -634,6 +676,21 @@ class ScenarioStatsAccumulator {
|
|
|
634
676
|
step.record(reply, observedLatencyMs);
|
|
635
677
|
return step.sortIndex;
|
|
636
678
|
}
|
|
679
|
+
/**
|
|
680
|
+
* Captures the bounded, private history view without building scenario
|
|
681
|
+
* steps, status rows, plugins, aliases, or any other public result shape.
|
|
682
|
+
*/
|
|
683
|
+
buildReportHistorySnapshot() {
|
|
684
|
+
const ok = this.ok.buildReportHistoryMeasurement();
|
|
685
|
+
const failed = this.fail.buildReportHistoryMeasurement();
|
|
686
|
+
return {
|
|
687
|
+
scenarioName: this.scenarioName,
|
|
688
|
+
sortIndex: this.sortIndex,
|
|
689
|
+
all: this.ok.buildCombinedReportHistoryMeasurement(this.fail),
|
|
690
|
+
...(ok.count > 0 ? { ok } : {}),
|
|
691
|
+
...(failed.count > 0 ? { failed } : {})
|
|
692
|
+
};
|
|
693
|
+
}
|
|
637
694
|
/**
|
|
638
695
|
* Builds the configured payload or helper object.
|
|
639
696
|
* Use this when all builder inputs are ready to be materialized.
|
|
@@ -3481,6 +3538,58 @@ export class LoadStrikeRunner {
|
|
|
3481
3538
|
licenseSession = await licenseClient.acquireLicenseLease(licensePayload);
|
|
3482
3539
|
const loggerSetup = createLoggerSetup(this.options.loggerConfig, this.options.minimumLogLevel, this.options, testInfo, nodeInfo);
|
|
3483
3540
|
const runLogger = loggerSetup.logger;
|
|
3541
|
+
const htmlReportHistoryRequested = (this.options.reportsEnabled ?? true)
|
|
3542
|
+
&& normalizeReportFormats(this.options.reportFormats ?? ["html", "txt", "csv", "md"]).includes("html");
|
|
3543
|
+
const distributedReportHistory = isDistributedReportHistoryExecution(clusterMode, this.options);
|
|
3544
|
+
let localReportInput = htmlReportHistoryRequested
|
|
3545
|
+
&& distributedReportHistory
|
|
3546
|
+
? distributedLocalReportInput()
|
|
3547
|
+
: emptyLocalReportInput();
|
|
3548
|
+
let reportHistoryCollector;
|
|
3549
|
+
let reportHistoryLifecycle;
|
|
3550
|
+
if (htmlReportHistoryRequested && !distributedReportHistory) {
|
|
3551
|
+
const warnReportHistoryFailure = (category, error) => {
|
|
3552
|
+
const exceptionClasses = error === undefined
|
|
3553
|
+
? ""
|
|
3554
|
+
: `; exception_classes=${sanitizedExceptionClassChain(error)}`;
|
|
3555
|
+
try {
|
|
3556
|
+
runLogger.warn(`LoadStrike report history unavailable: ${category}${exceptionClasses}.`);
|
|
3557
|
+
}
|
|
3558
|
+
catch {
|
|
3559
|
+
// Report-only diagnostics are best-effort.
|
|
3560
|
+
}
|
|
3561
|
+
};
|
|
3562
|
+
reportHistoryCollector = new ReportHistoryCollector({
|
|
3563
|
+
scenarioCount: selectedScenarios.length,
|
|
3564
|
+
onCaptureError: (error) => warnReportHistoryFailure("capture_failure", error)
|
|
3565
|
+
});
|
|
3566
|
+
if (!reportHistoryCollector.available) {
|
|
3567
|
+
warnReportHistoryFailure("budget_pressure");
|
|
3568
|
+
}
|
|
3569
|
+
const snapshotFactory = () => Array.from(scenarioAccumulators.values())
|
|
3570
|
+
.map((value) => value.buildReportHistorySnapshot())
|
|
3571
|
+
.sort((left, right) => (left.sortIndex ?? 0) - (right.sortIndex ?? 0)
|
|
3572
|
+
|| left.scenarioName.localeCompare(right.scenarioName));
|
|
3573
|
+
const reportHistoryWorker = new ReportHistoryWorker({
|
|
3574
|
+
collector: reportHistoryCollector,
|
|
3575
|
+
cadenceSeconds: resolvedReportHistoryCadenceSeconds(this.options.reportingIntervalSeconds ?? 5),
|
|
3576
|
+
snapshotFactory,
|
|
3577
|
+
onFailure: (category, exceptionClasses) => {
|
|
3578
|
+
try {
|
|
3579
|
+
runLogger.warn(`LoadStrike report history unavailable: ${category}; exception_classes=${exceptionClasses}.`);
|
|
3580
|
+
}
|
|
3581
|
+
catch {
|
|
3582
|
+
// Report-only diagnostics are best-effort.
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
});
|
|
3586
|
+
reportHistoryLifecycle = new ReportHistoryLifecycleCoordinator({
|
|
3587
|
+
scenarioCount: selectedScenarios.length,
|
|
3588
|
+
start: () => reportHistoryWorker.start(),
|
|
3589
|
+
stopAndFinalize: () => reportHistoryWorker.stopAndFinalize(),
|
|
3590
|
+
stopWithoutFinalizing: () => reportHistoryWorker.stopWithoutFinalizing()
|
|
3591
|
+
});
|
|
3592
|
+
}
|
|
3484
3593
|
const scenarioStartInfos = selectedScenarios.map((scenario, index) => {
|
|
3485
3594
|
const startInfo = {
|
|
3486
3595
|
scenarioName: scenario.name,
|
|
@@ -3590,7 +3699,7 @@ export class LoadStrikeRunner {
|
|
|
3590
3699
|
realtimeInFlight = false;
|
|
3591
3700
|
}
|
|
3592
3701
|
};
|
|
3593
|
-
const reportingIntervalMs =
|
|
3702
|
+
const reportingIntervalMs = boundedReportingIntervalMs(this.options.reportingIntervalSeconds ?? 5);
|
|
3594
3703
|
const triggerRealtimeSnapshot = () => {
|
|
3595
3704
|
if (realtimeCurrent) {
|
|
3596
3705
|
return;
|
|
@@ -3677,6 +3786,7 @@ export class LoadStrikeRunner {
|
|
|
3677
3786
|
iterationObservationRunId,
|
|
3678
3787
|
iterationObservationResultOwnerId,
|
|
3679
3788
|
iterationObservationProcessGroup,
|
|
3789
|
+
reportHistoryLifecycle,
|
|
3680
3790
|
executeScenarioInvocation: (targetScenario, context, operation) => this.executeScenarioInvocation(targetScenario, context, operation),
|
|
3681
3791
|
invokeBeforeScenario: (runtimePolicies, scenarioName) => this.invokeBeforeScenario(runtimePolicies, scenarioName, policyErrors, runtimePolicyErrorMode),
|
|
3682
3792
|
invokeAfterScenario: (runtimePolicies, scenarioName, stats) => this.invokeAfterScenario(runtimePolicies, scenarioName, stats, policyErrors, runtimePolicyErrorMode),
|
|
@@ -3764,9 +3874,14 @@ export class LoadStrikeRunner {
|
|
|
3764
3874
|
...iterationObservationReporter.buildWarnings()
|
|
3765
3875
|
];
|
|
3766
3876
|
result.pluginsData = mergePluginData(result.pluginsData, await this.collectPluginData(plugins, attachRunResultAliases(result), pluginLifecycleErrors));
|
|
3877
|
+
if (reportHistoryCollector) {
|
|
3878
|
+
localReportInput = {
|
|
3879
|
+
history: reportHistoryCollector.toProjection()
|
|
3880
|
+
};
|
|
3881
|
+
}
|
|
3767
3882
|
const finalizedResult = attachRunResultAliases(result);
|
|
3768
3883
|
finalizedResult.logFiles = mergeStringArrays(finalizedResult.logFiles, loggerSetup.logFiles);
|
|
3769
|
-
finalizedResult.reportFiles = this.writeReports(finalizedResult);
|
|
3884
|
+
finalizedResult.reportFiles = this.writeReports(finalizedResult, localReportInput);
|
|
3770
3885
|
finalizedResult.disabledSinks = sinkStates.filter((x) => x.disabled).map((x) => x.name);
|
|
3771
3886
|
await this.emitRunResult(sinkStates, finalizedResult, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3772
3887
|
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
@@ -3777,6 +3892,7 @@ export class LoadStrikeRunner {
|
|
|
3777
3892
|
return finalizedResult;
|
|
3778
3893
|
}
|
|
3779
3894
|
finally {
|
|
3895
|
+
reportHistoryLifecycle?.stopWithoutFinalizing();
|
|
3780
3896
|
await stopRealtimeReporting();
|
|
3781
3897
|
if (!iterationObservationsFinalized) {
|
|
3782
3898
|
await iterationObservationReporter.sealAndDrain().catch(() => { });
|
|
@@ -4254,7 +4370,7 @@ export class LoadStrikeRunner {
|
|
|
4254
4370
|
message: String(error ?? "runtime policy callback failed")
|
|
4255
4371
|
});
|
|
4256
4372
|
}
|
|
4257
|
-
writeReports(result) {
|
|
4373
|
+
writeReports(result, localReportInput = emptyLocalReportInput()) {
|
|
4258
4374
|
const reportsEnabled = this.options.reportsEnabled ?? true;
|
|
4259
4375
|
if (!reportsEnabled) {
|
|
4260
4376
|
return [];
|
|
@@ -4279,7 +4395,7 @@ export class LoadStrikeRunner {
|
|
|
4279
4395
|
writeFileSync(path, buildDotnetMarkdownReport(nodeStats), "utf8");
|
|
4280
4396
|
}
|
|
4281
4397
|
else if (format === "html") {
|
|
4282
|
-
writeFileSync(path, buildDotnetHtmlReport(nodeStats), "utf8");
|
|
4398
|
+
writeFileSync(path, buildDotnetHtmlReport(nodeStats, localReportInput), "utf8");
|
|
4283
4399
|
}
|
|
4284
4400
|
written.push(path);
|
|
4285
4401
|
}
|
|
@@ -4312,6 +4428,19 @@ function hasPluginRows(value) {
|
|
|
4312
4428
|
}
|
|
4313
4429
|
return value.tables.some((table) => Array.isArray(table.rows) && table.rows.length > 0);
|
|
4314
4430
|
}
|
|
4431
|
+
function boundedReportingIntervalMs(seconds) {
|
|
4432
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
4433
|
+
return 1;
|
|
4434
|
+
}
|
|
4435
|
+
const milliseconds = seconds * 1000;
|
|
4436
|
+
if (!Number.isFinite(milliseconds) || milliseconds >= 2147483647) {
|
|
4437
|
+
return 2147483647;
|
|
4438
|
+
}
|
|
4439
|
+
return Math.max(Math.trunc(milliseconds), 1);
|
|
4440
|
+
}
|
|
4441
|
+
function resolvedReportHistoryCadenceSeconds(seconds) {
|
|
4442
|
+
return boundedReportingIntervalMs(seconds) / 1000;
|
|
4443
|
+
}
|
|
4315
4444
|
async function executeV2FixedArrivals(args) {
|
|
4316
4445
|
const { rate, intervalNs, totalArrivals, budget, cancellationToken, shouldStopNow, nextInstanceInfo, runBombingInvocation, logger, scenarioName, deadlineOffsetsNs, tolerancesNs, ownedOrdinals, shardIndex = 0, shardCount = 1, telemetry, segment } = args;
|
|
4317
4446
|
const segmentStartNs = process.hrtime.bigint();
|
|
@@ -4448,7 +4577,15 @@ function maxBigInt(left, right) {
|
|
|
4448
4577
|
return left > right ? left : right;
|
|
4449
4578
|
}
|
|
4450
4579
|
async function executeScenarioRuntime(args) {
|
|
4451
|
-
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;
|
|
4580
|
+
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;
|
|
4581
|
+
let reportHistoryExecutionEnded = false;
|
|
4582
|
+
const endReportHistoryExecution = async () => {
|
|
4583
|
+
if (reportHistoryExecutionEnded) {
|
|
4584
|
+
return;
|
|
4585
|
+
}
|
|
4586
|
+
reportHistoryExecutionEnded = true;
|
|
4587
|
+
await reportHistoryLifecycle?.scenarioExecutionEnded();
|
|
4588
|
+
};
|
|
4452
4589
|
const scenarioStartedMs = Date.now();
|
|
4453
4590
|
const scenarioContextData = {};
|
|
4454
4591
|
const registeredMetrics = [];
|
|
@@ -5269,6 +5406,7 @@ async function executeScenarioRuntime(args) {
|
|
|
5269
5406
|
})));
|
|
5270
5407
|
await invokeBeforeScenario(policies, scenario.name);
|
|
5271
5408
|
await runWarmUpAsync();
|
|
5409
|
+
reportHistoryLifecycle?.scenarioBombingStarted();
|
|
5272
5410
|
accumulator.setCurrentOperation("Bombing");
|
|
5273
5411
|
const simulations = scenario.getSimulations();
|
|
5274
5412
|
if (!simulations.length) {
|
|
@@ -5302,6 +5440,7 @@ async function executeScenarioRuntime(args) {
|
|
|
5302
5440
|
}
|
|
5303
5441
|
}
|
|
5304
5442
|
accumulator.setCurrentOperation(stopScenario || stopTestState.value ? "Stop" : "Complete");
|
|
5443
|
+
await endReportHistoryExecution();
|
|
5305
5444
|
await invokeAfterScenario(policies, scenario.name, runtime);
|
|
5306
5445
|
}
|
|
5307
5446
|
catch (error) {
|
|
@@ -5318,6 +5457,7 @@ async function executeScenarioRuntime(args) {
|
|
|
5318
5457
|
}
|
|
5319
5458
|
}
|
|
5320
5459
|
finally {
|
|
5460
|
+
await endReportHistoryExecution();
|
|
5321
5461
|
scenarioDurationsMs.set(scenario.name, Math.max(Date.now() - scenarioStartedMs, 0));
|
|
5322
5462
|
try {
|
|
5323
5463
|
await scenario.invokeClean({
|
|
@@ -7251,6 +7391,11 @@ function resolveClusterExecutionMode(options) {
|
|
|
7251
7391
|
}
|
|
7252
7392
|
return "single";
|
|
7253
7393
|
}
|
|
7394
|
+
function isDistributedReportHistoryExecution(clusterMode, options) {
|
|
7395
|
+
return clusterMode !== "single"
|
|
7396
|
+
|| options.loadEngineV2SegmentLifecycleOverride !== undefined
|
|
7397
|
+
|| Math.max(Math.trunc(options.clusterShardCount ?? 1), 1) > 1;
|
|
7398
|
+
}
|
|
7254
7399
|
function buildEmptyNodeStats(args) {
|
|
7255
7400
|
return attachNodeStatsAliases({
|
|
7256
7401
|
startedUtc: args.startedUtc,
|
|
@@ -11082,6 +11227,7 @@ function buildGroupedCorrelationRows(rows) {
|
|
|
11082
11227
|
LatencyMinMs: latencySamples.length ? formatOptionalLatency(latencySamples[0]) : "",
|
|
11083
11228
|
LatencyMeanMs: latencySamples.length ? formatOptionalLatency(latencySamples.reduce((sum, value) => sum + value, 0) / latencySamples.length) : "",
|
|
11084
11229
|
LatencyP50Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.5)) : "",
|
|
11230
|
+
LatencyP75Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.75)) : "",
|
|
11085
11231
|
LatencyP80Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.8)) : "",
|
|
11086
11232
|
LatencyP85Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.85)) : "",
|
|
11087
11233
|
LatencyP90Ms: latencySamples.length ? formatOptionalLatency(percentile(latencySamples, 0.9)) : "",
|
|
@@ -11294,6 +11440,8 @@ export const __loadstrikeTestExports = {
|
|
|
11294
11440
|
buildThresholdCheckExpression,
|
|
11295
11441
|
buildTrackingLeaseKey,
|
|
11296
11442
|
buildTrackingRunNamespace,
|
|
11443
|
+
boundedReportingIntervalMs,
|
|
11444
|
+
resolvedReportHistoryCadenceSeconds,
|
|
11297
11445
|
clusterNodeResultToNodeStats,
|
|
11298
11446
|
combineAbortSignals,
|
|
11299
11447
|
computeScenarioRequestCount,
|
|
@@ -11313,6 +11461,7 @@ export const __loadstrikeTestExports = {
|
|
|
11313
11461
|
formatUtcReportTimestamp,
|
|
11314
11462
|
hasPluginRows,
|
|
11315
11463
|
inferRuntimeLegacyHttpResponseSource,
|
|
11464
|
+
isDistributedReportHistoryExecution,
|
|
11316
11465
|
isComparisonFailed,
|
|
11317
11466
|
loadJsonObject,
|
|
11318
11467
|
logLevelOrder,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ReportHistoryProjection } from "./report-history.js";
|
|
2
|
+
export interface LocalReportInput {
|
|
3
|
+
history: ReportHistoryProjection;
|
|
4
|
+
}
|
|
5
|
+
export declare function emptyLocalReportInput(): LocalReportInput;
|
|
6
|
+
export declare function distributedLocalReportInput(): LocalReportInput;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export declare const REPORT_HISTORY_MAX_POINTS = 2048;
|
|
2
|
+
export declare const REPORT_HISTORY_MAX_SCALAR_VALUES = 262144;
|
|
3
|
+
export type ReportHistoryReasonCategory = "capture_failure" | "budget_pressure" | "distributed_temporal_aggregation_unavailable";
|
|
4
|
+
export interface ReportHistoryMeasurement {
|
|
5
|
+
count: number;
|
|
6
|
+
bytes: number;
|
|
7
|
+
approximate?: boolean;
|
|
8
|
+
percent50Ms?: number;
|
|
9
|
+
percent75Ms?: number;
|
|
10
|
+
percent95Ms?: number;
|
|
11
|
+
percent99Ms?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ReportHistoryScenario {
|
|
14
|
+
scenarioName: string;
|
|
15
|
+
sortIndex?: number;
|
|
16
|
+
all?: ReportHistoryMeasurement;
|
|
17
|
+
ok?: ReportHistoryMeasurement;
|
|
18
|
+
failed?: ReportHistoryMeasurement;
|
|
19
|
+
}
|
|
20
|
+
export interface ReportHistoryPoint {
|
|
21
|
+
elapsedSeconds: number;
|
|
22
|
+
terminal: boolean;
|
|
23
|
+
scenarios: ReportHistoryScenario[];
|
|
24
|
+
}
|
|
25
|
+
export interface AvailableReportHistoryProjection {
|
|
26
|
+
status: "available";
|
|
27
|
+
points: ReportHistoryPoint[];
|
|
28
|
+
}
|
|
29
|
+
export interface UnavailableReportHistoryProjection {
|
|
30
|
+
status: "unavailable";
|
|
31
|
+
reasonCategory: ReportHistoryReasonCategory;
|
|
32
|
+
points: [];
|
|
33
|
+
}
|
|
34
|
+
export type ReportHistoryProjection = AvailableReportHistoryProjection | UnavailableReportHistoryProjection;
|
|
35
|
+
export interface ReportHistoryRateSeries {
|
|
36
|
+
scenarioName: string;
|
|
37
|
+
values: Array<number | null>;
|
|
38
|
+
}
|
|
39
|
+
export interface ReportHistoryCollectorOptions {
|
|
40
|
+
scenarioCount: number;
|
|
41
|
+
maxPoints?: number;
|
|
42
|
+
nowNs?: () => bigint;
|
|
43
|
+
onCaptureError?: (error: unknown) => void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Bounded report-only cumulative telemetry. This type is intentionally not
|
|
47
|
+
* exported from the package entry point and never participates in public run,
|
|
48
|
+
* sink, observation, or cluster payloads.
|
|
49
|
+
*/
|
|
50
|
+
export declare class ReportHistoryCollector {
|
|
51
|
+
readonly pointLimit: number;
|
|
52
|
+
private readonly nowNs;
|
|
53
|
+
private readonly onCaptureError?;
|
|
54
|
+
private points;
|
|
55
|
+
private startNs;
|
|
56
|
+
private terminalCaptured;
|
|
57
|
+
private reasonCategory;
|
|
58
|
+
constructor(options: ReportHistoryCollectorOptions);
|
|
59
|
+
get available(): boolean;
|
|
60
|
+
start(startNs?: bigint): void;
|
|
61
|
+
capture(snapshotFactory: () => readonly ReportHistoryScenario[]): boolean;
|
|
62
|
+
finalize(snapshotFactory: () => readonly ReportHistoryScenario[]): boolean;
|
|
63
|
+
disable(reasonCategory: ReportHistoryReasonCategory): void;
|
|
64
|
+
toProjection(): ReportHistoryProjection;
|
|
65
|
+
private captureSafely;
|
|
66
|
+
private compact;
|
|
67
|
+
}
|
|
68
|
+
export interface ReportHistoryWorkerOptions {
|
|
69
|
+
collector: ReportHistoryCollector;
|
|
70
|
+
cadenceSeconds: number;
|
|
71
|
+
snapshotFactory: () => readonly ReportHistoryScenario[];
|
|
72
|
+
nowNs?: () => bigint;
|
|
73
|
+
onFailure?: (category: ReportHistoryReasonCategory, exceptionClasses: string) => void;
|
|
74
|
+
}
|
|
75
|
+
interface ReportHistoryLifecycleWorker {
|
|
76
|
+
start(): void;
|
|
77
|
+
stopAndFinalize(): void;
|
|
78
|
+
stopWithoutFinalizing(): void;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Coordinates one private history worker across every local scenario. The
|
|
82
|
+
* first measured phase establishes the history clock, and the last scenario
|
|
83
|
+
* leaving execution records the terminal point before scenario cleanup.
|
|
84
|
+
*/
|
|
85
|
+
export declare class ReportHistoryLifecycleCoordinator {
|
|
86
|
+
private readonly worker;
|
|
87
|
+
private readonly scenarioCount;
|
|
88
|
+
private readonly terminalBoundary;
|
|
89
|
+
private releaseTerminalBoundary;
|
|
90
|
+
private measuredLoadStarted;
|
|
91
|
+
private endedScenarios;
|
|
92
|
+
private completed;
|
|
93
|
+
private terminalBoundaryReleased;
|
|
94
|
+
constructor(worker: ReportHistoryLifecycleWorker & {
|
|
95
|
+
scenarioCount?: number;
|
|
96
|
+
});
|
|
97
|
+
scenarioBombingStarted(): void;
|
|
98
|
+
scenarioExecutionEnded(): Promise<void>;
|
|
99
|
+
stopWithoutFinalizing(): void;
|
|
100
|
+
private releaseBoundary;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Owns the report timer. Every cadence calculation, timeout callback, snapshot,
|
|
104
|
+
* and projection is exception-bounded so report history can never fail a run.
|
|
105
|
+
*/
|
|
106
|
+
export declare class ReportHistoryWorker {
|
|
107
|
+
private readonly options;
|
|
108
|
+
private readonly nowNs;
|
|
109
|
+
private timer;
|
|
110
|
+
private cadenceNs;
|
|
111
|
+
private nextDeadlineNs;
|
|
112
|
+
private running;
|
|
113
|
+
constructor(options: ReportHistoryWorkerOptions);
|
|
114
|
+
get started(): boolean;
|
|
115
|
+
start(): void;
|
|
116
|
+
stopAndFinalize(): void;
|
|
117
|
+
stopWithoutFinalizing(): void;
|
|
118
|
+
private scheduleNext;
|
|
119
|
+
private onTimer;
|
|
120
|
+
private fail;
|
|
121
|
+
}
|
|
122
|
+
export declare function sanitizedExceptionClassChain(error: unknown): string;
|
|
123
|
+
export declare function deriveScenarioRates(points: readonly ReportHistoryPoint[]): ReportHistoryRateSeries[];
|
|
124
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LocalReportInput } from "./local-report-input.js";
|
|
1
2
|
type ReportRecord = Record<string, any>;
|
|
2
3
|
type ReportTab = [string, string, string];
|
|
3
4
|
declare function buildUngroupedCorrelationChartPayload(rows: ReportRecord[]): ReportRecord;
|
|
@@ -14,7 +15,7 @@ declare function buildDotnetMetricHtml(nodeStats: ReportRecord): string;
|
|
|
14
15
|
declare function buildDotnetGroupedCorrelationSummaryHtml(rows: ReportRecord[], groupedChartKey: string): string;
|
|
15
16
|
declare function buildDotnetUngroupedCorrelationSummaryHtml(rows: ReportRecord[], groupedChartKey: string): string;
|
|
16
17
|
declare function buildDotnetPluginHints(plugin: ReportRecord): string;
|
|
17
|
-
declare function buildDotnetHtmlTabs(nodeStats: ReportRecord): ReportTab[];
|
|
18
|
+
declare function buildDotnetHtmlTabs(nodeStats: ReportRecord, localReportInput?: LocalReportInput): ReportTab[];
|
|
18
19
|
/**
|
|
19
20
|
* Exposes the build dotnet txt report operation. Use this when interacting with the SDK through this surface.
|
|
20
21
|
*/
|
|
@@ -28,9 +29,11 @@ export declare function buildDotnetCsvReport(nodeStats: ReportRecord): string;
|
|
|
28
29
|
*/
|
|
29
30
|
export declare function buildDotnetMarkdownReport(nodeStats: ReportRecord): string;
|
|
30
31
|
/**
|
|
31
|
-
*
|
|
32
|
+
* Builds the portable single-file HTML report. The optional second argument is
|
|
33
|
+
* an internal report-only carrier used by the native runner and is never added
|
|
34
|
+
* to public result, sink, observation, or cluster payloads.
|
|
32
35
|
*/
|
|
33
|
-
export declare function buildDotnetHtmlReport(nodeStats: ReportRecord): string;
|
|
36
|
+
export declare function buildDotnetHtmlReport(nodeStats: ReportRecord, localReportInput?: LocalReportInput): string;
|
|
34
37
|
export declare const __loadstrikeTestExports: {
|
|
35
38
|
buildDotnetFailedResponseContent: typeof buildDotnetFailedResponseContent;
|
|
36
39
|
buildDotnetFailedResponseHtml: typeof buildDotnetFailedResponseHtml;
|
package/dist/types/runtime.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ClusterNodeResult, type LoadEngineV2PlanInput } from "./cluster.js";
|
|
2
2
|
import { RedisCorrelationStore, RedisCorrelationStoreOptions, TrackingFieldSelector, type TrackingPayload } from "./correlation.js";
|
|
3
3
|
import { type EndpointAdapter, type EndpointDefinition } from "./transports.js";
|
|
4
|
+
import { type ReportHistoryMeasurement, type ReportHistoryScenario } from "./report-history.js";
|
|
4
5
|
import { LoadEngineV2ExecutionBudget, type LoadEngineV2DistributionRecord, type LoadStrikeHistogramV1Sidecar } from "./load-engine-v2.js";
|
|
5
6
|
import { type LoadStrikeIterationObservationBatchV1, type LoadStrikeIterationObservationStreamCompletionV1, type LoadStrikeIterationStepObservationV1, type LoadStrikeObservationDeliveryStats } from "./iteration-observations.js";
|
|
6
7
|
declare const LOAD_ENGINE_V2_SCHEDULER_DISTRIBUTIONS: unique symbol;
|
|
@@ -570,6 +571,17 @@ declare class MeasurementAccumulator {
|
|
|
570
571
|
*/
|
|
571
572
|
build(allRequestCount: number, durationMs: number): LoadStrikeMeasurementStats;
|
|
572
573
|
buildCombined(other: MeasurementAccumulator, allRequestCount: number, durationMs: number): LoadStrikeMeasurementStats;
|
|
574
|
+
/**
|
|
575
|
+
* Projects only the cumulative fields used by the private HTML-report
|
|
576
|
+
* history. Legacy distributions deliberately omit latency so this cadence
|
|
577
|
+
* path never scans their retained per-request arrays.
|
|
578
|
+
*/
|
|
579
|
+
buildReportHistoryMeasurement(): ReportHistoryMeasurement;
|
|
580
|
+
/**
|
|
581
|
+
* Combines only bounded native latency state plus exact counters. It does
|
|
582
|
+
* not materialize status rows or any public measurement DTO.
|
|
583
|
+
*/
|
|
584
|
+
buildCombinedReportHistoryMeasurement(other: MeasurementAccumulator): ReportHistoryMeasurement;
|
|
573
585
|
private histogramSnapshot;
|
|
574
586
|
}
|
|
575
587
|
declare class StepStatsAccumulator {
|
|
@@ -627,6 +639,11 @@ declare class ScenarioStatsAccumulator {
|
|
|
627
639
|
* Use this when the surrounding wrapper type makes this operation the clearest way to express your intent.
|
|
628
640
|
*/
|
|
629
641
|
recordStep(stepName: string, reply: LoadStrikeStepReply, observedLatencyMs: number, sortIndex?: number): number;
|
|
642
|
+
/**
|
|
643
|
+
* Captures the bounded, private history view without building scenario
|
|
644
|
+
* steps, status rows, plugins, aliases, or any other public result shape.
|
|
645
|
+
*/
|
|
646
|
+
buildReportHistorySnapshot(): ReportHistoryScenario;
|
|
630
647
|
/**
|
|
631
648
|
* Builds the configured payload or helper object.
|
|
632
649
|
* Use this when all builder inputs are ready to be materialized.
|
|
@@ -2384,6 +2401,8 @@ export declare class LoadStrikeRunner {
|
|
|
2384
2401
|
private collectPluginData;
|
|
2385
2402
|
}
|
|
2386
2403
|
declare function hasPluginRows(value: LoadStrikePluginData): boolean;
|
|
2404
|
+
declare function boundedReportingIntervalMs(seconds: number): number;
|
|
2405
|
+
declare function resolvedReportHistoryCadenceSeconds(seconds: number): number;
|
|
2387
2406
|
declare function combineAbortSignals(...signals: AbortSignal[]): AbortSignal;
|
|
2388
2407
|
declare function delayWithAbort(durationMs: number, signal: AbortSignal): Promise<void>;
|
|
2389
2408
|
declare function createRuntimeRandom(): LoadStrikeRandom;
|
|
@@ -2434,6 +2453,7 @@ declare function normalizeThresholdScope(value: string): "scenario" | "step" | "
|
|
|
2434
2453
|
declare function buildThresholdCheckExpression(scope: "scenario" | "step" | "metric", stepName: string, field: string): string;
|
|
2435
2454
|
declare function detailedToNodeStats(result: LoadStrikeRunResult, metricStats?: LoadStrikeMetricStats): LoadStrikeNodeStats;
|
|
2436
2455
|
declare function resolveClusterExecutionMode(options: LoadStrikeRunnerOptions): "single" | "local-coordinator" | "nats-coordinator" | "nats-agent";
|
|
2456
|
+
declare function isDistributedReportHistoryExecution(clusterMode: ReturnType<typeof resolveClusterExecutionMode>, options: Pick<LoadStrikeRunnerOptions, "clusterShardCount" | "loadEngineV2SegmentLifecycleOverride">): boolean;
|
|
2437
2457
|
declare function buildEmptyNodeStats(args: {
|
|
2438
2458
|
startedUtc: string;
|
|
2439
2459
|
completedUtc: string;
|
|
@@ -2644,6 +2664,8 @@ export declare const __loadstrikeTestExports: {
|
|
|
2644
2664
|
buildThresholdCheckExpression: typeof buildThresholdCheckExpression;
|
|
2645
2665
|
buildTrackingLeaseKey: typeof buildTrackingLeaseKey;
|
|
2646
2666
|
buildTrackingRunNamespace: typeof buildTrackingRunNamespace;
|
|
2667
|
+
boundedReportingIntervalMs: typeof boundedReportingIntervalMs;
|
|
2668
|
+
resolvedReportHistoryCadenceSeconds: typeof resolvedReportHistoryCadenceSeconds;
|
|
2647
2669
|
clusterNodeResultToNodeStats: typeof clusterNodeResultToNodeStats;
|
|
2648
2670
|
combineAbortSignals: typeof combineAbortSignals;
|
|
2649
2671
|
computeScenarioRequestCount: typeof computeScenarioRequestCount;
|
|
@@ -2663,6 +2685,7 @@ export declare const __loadstrikeTestExports: {
|
|
|
2663
2685
|
formatUtcReportTimestamp: typeof formatUtcReportTimestamp;
|
|
2664
2686
|
hasPluginRows: typeof hasPluginRows;
|
|
2665
2687
|
inferRuntimeLegacyHttpResponseSource: typeof inferRuntimeLegacyHttpResponseSource;
|
|
2688
|
+
isDistributedReportHistoryExecution: typeof isDistributedReportHistoryExecution;
|
|
2666
2689
|
isComparisonFailed: typeof isComparisonFailed;
|
|
2667
2690
|
loadJsonObject: typeof loadJsonObject;
|
|
2668
2691
|
logLevelOrder: typeof logLevelOrder;
|
package/package.json
CHANGED