@loadstrike/loadstrike-sdk 1.0.30401 → 1.0.31001
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 +4 -0
- package/dist/cjs/iteration-observation-diagnostics.js +513 -0
- package/dist/cjs/iteration-observations.js +195 -22
- package/dist/cjs/local.js +55 -10
- package/dist/cjs/reporting.js +26 -7
- package/dist/cjs/runtime.js +159 -80
- 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.js +55 -10
- package/dist/esm/reporting.js +26 -7
- package/dist/esm/runtime.js +159 -80
- 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/runtime.d.ts +2 -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/esm/runtime.js
CHANGED
|
@@ -10,6 +10,8 @@ import { buildDotnetCsvReport, buildDotnetHtmlReport, buildDotnetMarkdownReport,
|
|
|
10
10
|
import { PortalReportingSink, cloneReportingSinkForRun } from "./sinks.js";
|
|
11
11
|
import { LoadEngineV2ExecutionBudget, buildLoadEngineV2TrafficMixSeedId, LoadStrikeHistogramV1, parseLoadEngineV2HistogramArtifact, serializeLoadEngineV2HistogramArtifact, classifyLoadEngineV2Arrival, loadEngineV2FixedArrivalCount, loadEngineV2FixedDeadlineNs, loadEngineV2LatenessToleranceNs, loadEngineV2TrafficMixLaneUnitCount, loadEngineV2TrafficMixOwnedUnits, planRampingInjectionDeadlines, planRampingConstantDeadlines, planRandomInjectionDeadlines, fnv1a32 } from "./load-engine-v2.js";
|
|
12
12
|
import { DEFAULT_ITERATION_OBSERVATION_SETTINGS, IterationObservationReporter, createIterationObservation, createIterationStepObservation, utcNowNs, validateIterationObservationSettings } from "./iteration-observations.js";
|
|
13
|
+
import { logIterationObservationFailure, logIterationObservationRecovery } from "./iteration-observation-diagnostics.js";
|
|
14
|
+
import { normalizeSinkRetryBackoffMs, normalizeSinkRetryCount, sinkRetryDelayMs, waitForSinkRetryDelay } from "./sink-retry-policy.js";
|
|
13
15
|
const LOAD_ENGINE_V2_SCHEDULER_DISTRIBUTIONS = Symbol("loadstrike.load-engine-v2.scheduler-distributions");
|
|
14
16
|
const PORTAL_RUN_TOKEN_PROVIDER = Symbol.for("loadstrike.internal.portal-run-token-provider");
|
|
15
17
|
export const LoadStrikeNodeType = {
|
|
@@ -3427,8 +3429,8 @@ export class LoadStrikeRunner {
|
|
|
3427
3429
|
}));
|
|
3428
3430
|
const sinkErrors = [];
|
|
3429
3431
|
const policyErrors = [];
|
|
3430
|
-
const sinkRetryCount =
|
|
3431
|
-
const sinkRetryBackoffMs =
|
|
3432
|
+
const sinkRetryCount = normalizeSinkRetryCount(this.options.sinkRetryCount);
|
|
3433
|
+
const sinkRetryBackoffMs = normalizeSinkRetryBackoffMs(this.options.sinkRetryBackoffMs);
|
|
3432
3434
|
const policies = this.options.runtimePolicies ?? [];
|
|
3433
3435
|
const runtimePolicyErrorMode = normalizedRuntimePolicyErrorMode(this.options.runtimePolicyErrorMode);
|
|
3434
3436
|
const plugins = this.options.reportingSinks === undefined && this.options.workerPlugins === undefined &&
|
|
@@ -3522,14 +3524,14 @@ export class LoadStrikeRunner {
|
|
|
3522
3524
|
await init(baseContext, this.options.infraConfig ?? {});
|
|
3523
3525
|
}
|
|
3524
3526
|
}
|
|
3525
|
-
await this.initializeSinks(sinkStates, baseContext, this.options.infraConfig ?? {}, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3527
|
+
await this.initializeSinks(sinkStates, baseContext, this.options.infraConfig ?? {}, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3526
3528
|
for (const plugin of plugins) {
|
|
3527
3529
|
const start = resolveWorkerPluginStart(plugin);
|
|
3528
3530
|
if (start) {
|
|
3529
3531
|
await start(sessionInfo);
|
|
3530
3532
|
}
|
|
3531
3533
|
}
|
|
3532
|
-
await this.startSinks(sinkStates, sessionInfo, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3534
|
+
await this.startSinks(sinkStates, sessionInfo, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3533
3535
|
const iterationObservationRunId = String(this.internalOptions.iterationObservationRunId
|
|
3534
3536
|
?? sessionInfo.portalReportingRunId
|
|
3535
3537
|
?? sessionInfo.PortalReportingRunId
|
|
@@ -3560,7 +3562,10 @@ export class LoadStrikeRunner {
|
|
|
3560
3562
|
expectedResultOwnerCount64: iterationObservationExpectedResultOwnerCount64,
|
|
3561
3563
|
processGroup: iterationObservationProcessGroup,
|
|
3562
3564
|
settings: resolveIterationObservationSettings(this.options),
|
|
3563
|
-
sinks: reporterIterationObservationSinks
|
|
3565
|
+
sinks: reporterIterationObservationSinks,
|
|
3566
|
+
sinkRetryCount,
|
|
3567
|
+
sinkRetryBackoffMs,
|
|
3568
|
+
logger: runLogger
|
|
3564
3569
|
});
|
|
3565
3570
|
const emitRealtimeSnapshot = async () => {
|
|
3566
3571
|
if (realtimeInFlight) {
|
|
@@ -3572,7 +3577,7 @@ export class LoadStrikeRunner {
|
|
|
3572
3577
|
.map((value) => value.build(scenarioDurationsMs.get(value.scenarioName) ?? Math.max(Date.now() - started.getTime(), 0)))
|
|
3573
3578
|
.sort((left, right) => left.sortIndex - right.sortIndex);
|
|
3574
3579
|
const metricsSnapshot = collectMetricStats(allRegisteredMetrics, Date.now() - started.getTime());
|
|
3575
|
-
await this.emitRealtimeStats(sinkStates, snapshot, metricsSnapshot, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3580
|
+
await this.emitRealtimeStats(sinkStates, snapshot, metricsSnapshot, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3576
3581
|
if (toBoolean(this.options.displayConsoleMetrics, true)) {
|
|
3577
3582
|
const requestCount = snapshot.reduce((sum, value) => sum + value.allRequestCount, 0);
|
|
3578
3583
|
const okCount = snapshot.reduce((sum, value) => sum + value.allOkCount, 0);
|
|
@@ -3763,8 +3768,8 @@ export class LoadStrikeRunner {
|
|
|
3763
3768
|
finalizedResult.logFiles = mergeStringArrays(finalizedResult.logFiles, loggerSetup.logFiles);
|
|
3764
3769
|
finalizedResult.reportFiles = this.writeReports(finalizedResult);
|
|
3765
3770
|
finalizedResult.disabledSinks = sinkStates.filter((x) => x.disabled).map((x) => x.name);
|
|
3766
|
-
await this.emitRunResult(sinkStates, finalizedResult, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3767
|
-
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3771
|
+
await this.emitRunResult(sinkStates, finalizedResult, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3772
|
+
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3768
3773
|
sinksStopped = true;
|
|
3769
3774
|
finalizedResult.disabledSinks = sinkStates.filter((x) => x.disabled).map((x) => x.name);
|
|
3770
3775
|
finalizedResult.sinkErrors = sinkErrors
|
|
@@ -3777,7 +3782,7 @@ export class LoadStrikeRunner {
|
|
|
3777
3782
|
await iterationObservationReporter.sealAndDrain().catch(() => { });
|
|
3778
3783
|
}
|
|
3779
3784
|
if (!sinksStopped) {
|
|
3780
|
-
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors);
|
|
3785
|
+
await this.stopSinks(sinkStates, sinkRetryCount, sinkRetryBackoffMs, sinkErrors, testInfo.sessionId, runLogger);
|
|
3781
3786
|
}
|
|
3782
3787
|
if (!pluginsStopped) {
|
|
3783
3788
|
await this.stopPlugins(plugins, pluginLifecycleErrors, runLogger);
|
|
@@ -4085,9 +4090,9 @@ export class LoadStrikeRunner {
|
|
|
4085
4090
|
}
|
|
4086
4091
|
return filtered;
|
|
4087
4092
|
}
|
|
4088
|
-
async initializeSinks(sinkStates, context, infraConfig, retryCount, retryBackoffMs, sinkErrors) {
|
|
4093
|
+
async initializeSinks(sinkStates, context, infraConfig, retryCount, retryBackoffMs, sinkErrors, runId, logger) {
|
|
4089
4094
|
for (const state of sinkStates) {
|
|
4090
|
-
await this.invokeSinkAction(state, "init", retryCount, retryBackoffMs, sinkErrors, false, true, async () => {
|
|
4095
|
+
await this.invokeSinkAction(state, "init", retryCount, retryBackoffMs, sinkErrors, runId, logger, false, true, async () => {
|
|
4091
4096
|
const init = resolveSinkInit(state.sink);
|
|
4092
4097
|
if (init) {
|
|
4093
4098
|
await init(context, infraConfig);
|
|
@@ -4095,9 +4100,9 @@ export class LoadStrikeRunner {
|
|
|
4095
4100
|
});
|
|
4096
4101
|
}
|
|
4097
4102
|
}
|
|
4098
|
-
async startSinks(sinkStates, session, retryCount, retryBackoffMs, sinkErrors) {
|
|
4103
|
+
async startSinks(sinkStates, session, retryCount, retryBackoffMs, sinkErrors, runId, logger) {
|
|
4099
4104
|
for (const state of sinkStates) {
|
|
4100
|
-
await this.invokeSinkAction(state, "start", retryCount, retryBackoffMs, sinkErrors, false, true, async () => {
|
|
4105
|
+
await this.invokeSinkAction(state, "start", retryCount, retryBackoffMs, sinkErrors, runId, logger, false, true, async () => {
|
|
4101
4106
|
const start = resolveSinkStart(state.sink);
|
|
4102
4107
|
if (start) {
|
|
4103
4108
|
await start(session);
|
|
@@ -4105,9 +4110,9 @@ export class LoadStrikeRunner {
|
|
|
4105
4110
|
});
|
|
4106
4111
|
}
|
|
4107
4112
|
}
|
|
4108
|
-
async emitRealtimeStats(sinkStates, scenarioStats, metrics, retryCount, retryBackoffMs, sinkErrors) {
|
|
4113
|
+
async emitRealtimeStats(sinkStates, scenarioStats, metrics, retryCount, retryBackoffMs, sinkErrors, runId, logger) {
|
|
4109
4114
|
for (const state of sinkStates) {
|
|
4110
|
-
await this.invokeSinkAction(state, "realtime", retryCount, retryBackoffMs, sinkErrors, false, true, async () => {
|
|
4115
|
+
await this.invokeSinkAction(state, "realtime", retryCount, retryBackoffMs, sinkErrors, runId, logger, false, true, async () => {
|
|
4111
4116
|
const saveRealtimeStats = resolveSinkSaveRealtimeStats(state.sink);
|
|
4112
4117
|
if (saveRealtimeStats) {
|
|
4113
4118
|
await saveRealtimeStats(scenarioStats);
|
|
@@ -4119,10 +4124,9 @@ export class LoadStrikeRunner {
|
|
|
4119
4124
|
});
|
|
4120
4125
|
}
|
|
4121
4126
|
}
|
|
4122
|
-
async stopSinks(sinkStates, retryCount, retryBackoffMs, sinkErrors) {
|
|
4123
|
-
const shutdownRetryCount = 0;
|
|
4127
|
+
async stopSinks(sinkStates, retryCount, retryBackoffMs, sinkErrors, runId, logger) {
|
|
4124
4128
|
for (const state of sinkStates) {
|
|
4125
|
-
await this.invokeSinkAction(state, "stop",
|
|
4129
|
+
await this.invokeSinkAction(state, "stop", retryCount, retryBackoffMs, sinkErrors, runId, logger, true, true, async () => {
|
|
4126
4130
|
const stop = resolveSinkStop(state.sink);
|
|
4127
4131
|
if (stop) {
|
|
4128
4132
|
await stop();
|
|
@@ -4130,15 +4134,15 @@ export class LoadStrikeRunner {
|
|
|
4130
4134
|
});
|
|
4131
4135
|
const dispose = resolveSinkDispose(state.sink);
|
|
4132
4136
|
if (dispose) {
|
|
4133
|
-
await this.invokeSinkAction(state, "dispose",
|
|
4137
|
+
await this.invokeSinkAction(state, "dispose", retryCount, retryBackoffMs, sinkErrors, runId, logger, true, true, async () => {
|
|
4134
4138
|
await dispose();
|
|
4135
4139
|
});
|
|
4136
4140
|
}
|
|
4137
4141
|
}
|
|
4138
4142
|
}
|
|
4139
|
-
async emitRunResult(sinkStates, result, retryCount, retryBackoffMs, sinkErrors) {
|
|
4143
|
+
async emitRunResult(sinkStates, result, retryCount, retryBackoffMs, sinkErrors, runId, logger) {
|
|
4140
4144
|
for (const state of sinkStates) {
|
|
4141
|
-
await this.invokeSinkAction(state, "run-result", retryCount, retryBackoffMs, sinkErrors, false, false, async () => {
|
|
4145
|
+
await this.invokeSinkAction(state, "run-result", retryCount, retryBackoffMs, sinkErrors, runId, logger, false, false, async () => {
|
|
4142
4146
|
const saveRunResult = resolveSinkSaveRunResult(state.sink);
|
|
4143
4147
|
if (saveRunResult) {
|
|
4144
4148
|
await saveRunResult(result);
|
|
@@ -4146,23 +4150,49 @@ export class LoadStrikeRunner {
|
|
|
4146
4150
|
});
|
|
4147
4151
|
}
|
|
4148
4152
|
}
|
|
4149
|
-
async invokeSinkAction(state, phase, retryCount, retryBackoffMs, sinkErrors, ignoreDisabled, disableOnFailure, action) {
|
|
4153
|
+
async invokeSinkAction(state, phase, retryCount, retryBackoffMs, sinkErrors, runId, logger, ignoreDisabled, disableOnFailure, action) {
|
|
4150
4154
|
if (state.disabled && !ignoreDisabled) {
|
|
4151
4155
|
return;
|
|
4152
4156
|
}
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4157
|
+
const maximumAttempts = normalizeSinkRetryCount(retryCount) + 1;
|
|
4158
|
+
const backoffMs = normalizeSinkRetryBackoffMs(retryBackoffMs);
|
|
4159
|
+
for (let attempts = 1; attempts <= maximumAttempts; attempts += 1) {
|
|
4156
4160
|
try {
|
|
4157
4161
|
await action();
|
|
4162
|
+
if (attempts > 1) {
|
|
4163
|
+
logIterationObservationRecovery(logger, {
|
|
4164
|
+
sinkName: state.name,
|
|
4165
|
+
operation: "reporting-sink-action",
|
|
4166
|
+
phase,
|
|
4167
|
+
runId,
|
|
4168
|
+
resultOwnerId: "",
|
|
4169
|
+
observationCount: 0,
|
|
4170
|
+
attempt: attempts,
|
|
4171
|
+
maximumAttempts,
|
|
4172
|
+
nextDelayMs: 0
|
|
4173
|
+
});
|
|
4174
|
+
}
|
|
4158
4175
|
return;
|
|
4159
4176
|
}
|
|
4160
4177
|
catch (error) {
|
|
4161
|
-
|
|
4178
|
+
const exhausted = attempts >= maximumAttempts;
|
|
4179
|
+
const nextDelayMs = exhausted ? 0 : sinkRetryDelayMs(backoffMs, attempts);
|
|
4180
|
+
logIterationObservationFailure(logger, exhausted ? "error" : "warn", {
|
|
4181
|
+
sinkName: state.name,
|
|
4182
|
+
operation: "reporting-sink-action",
|
|
4183
|
+
phase,
|
|
4184
|
+
runId,
|
|
4185
|
+
resultOwnerId: "",
|
|
4186
|
+
observationCount: 0,
|
|
4187
|
+
attempt: attempts,
|
|
4188
|
+
maximumAttempts,
|
|
4189
|
+
nextDelayMs
|
|
4190
|
+
}, error);
|
|
4191
|
+
if (exhausted) {
|
|
4162
4192
|
sinkErrors.push({
|
|
4163
4193
|
sinkName: state.name,
|
|
4164
4194
|
phase,
|
|
4165
|
-
message:
|
|
4195
|
+
message: "The reporting sink action failed after retries.",
|
|
4166
4196
|
attempts
|
|
4167
4197
|
});
|
|
4168
4198
|
if (disableOnFailure) {
|
|
@@ -4170,9 +4200,7 @@ export class LoadStrikeRunner {
|
|
|
4170
4200
|
}
|
|
4171
4201
|
return;
|
|
4172
4202
|
}
|
|
4173
|
-
|
|
4174
|
-
await sleep(retryBackoffMs * attempts);
|
|
4175
|
-
}
|
|
4203
|
+
await waitForSinkRetryDelay(nextDelayMs);
|
|
4176
4204
|
}
|
|
4177
4205
|
}
|
|
4178
4206
|
}
|
|
@@ -4581,7 +4609,18 @@ async function executeScenarioRuntime(args) {
|
|
|
4581
4609
|
attachScenarioContextAliases(context);
|
|
4582
4610
|
const startedUtcNs = utcNowNs();
|
|
4583
4611
|
const startedAtNs = process.hrtime.bigint();
|
|
4584
|
-
|
|
4612
|
+
let policyFailure;
|
|
4613
|
+
let reply;
|
|
4614
|
+
try {
|
|
4615
|
+
reply = await executeScenarioInvocation(scenario, context, operation);
|
|
4616
|
+
}
|
|
4617
|
+
catch (error) {
|
|
4618
|
+
if (!(error instanceof RuntimePolicyCallbackError)) {
|
|
4619
|
+
throw error;
|
|
4620
|
+
}
|
|
4621
|
+
policyFailure = error;
|
|
4622
|
+
reply = LoadStrikeResponse.fail("runtime_policy_error", "", 0);
|
|
4623
|
+
}
|
|
4585
4624
|
const observedLatencyNs = maxBigInt(process.hrtime.bigint() - startedAtNs, 0n);
|
|
4586
4625
|
const completedUtcNs = startedUtcNs + observedLatencyNs;
|
|
4587
4626
|
const observedLatencyMs = Number(observedLatencyNs) / 1000000;
|
|
@@ -4596,7 +4635,8 @@ async function executeScenarioRuntime(args) {
|
|
|
4596
4635
|
observedLatencyUs: observedLatencyNs / 1000n,
|
|
4597
4636
|
reportedLatencyUs: normalizeRawObservationLatencyMicroseconds(resolveRecordedLatency(reply.customLatencyMs, observedLatencyMs)),
|
|
4598
4637
|
steps: attemptSteps,
|
|
4599
|
-
recordedSteps
|
|
4638
|
+
recordedSteps,
|
|
4639
|
+
...(policyFailure ? { policyFailure } : {})
|
|
4600
4640
|
};
|
|
4601
4641
|
};
|
|
4602
4642
|
const captureAttemptObservation = (operation, globalOrdinal, attemptIndex, isFinalAttempt, attempt, simulationIndex, simulationKind, iterationId, globalSecondaryOrdinal = 0n) => {
|
|
@@ -4645,22 +4685,18 @@ async function executeScenarioRuntime(args) {
|
|
|
4645
4685
|
const maxAttempts = 1 + (scenario.shouldRestartIterationOnFail() ? restartIterationMaxAttempts : 0);
|
|
4646
4686
|
while (attempts < maxAttempts && !shouldStopNow()) {
|
|
4647
4687
|
attempts += 1;
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
attempt
|
|
4651
|
-
}
|
|
4652
|
-
catch (error) {
|
|
4653
|
-
if (error instanceof RuntimePolicyCallbackError) {
|
|
4654
|
-
stopScenario = true;
|
|
4655
|
-
scenarioAbortController.abort(error);
|
|
4656
|
-
}
|
|
4657
|
-
throw error;
|
|
4658
|
-
}
|
|
4659
|
-
const shouldRetry = !attempt.reply.isSuccess
|
|
4688
|
+
const attempt = await runSingleInvocation("Bombing", instanceData, instanceNumber, instanceId, false);
|
|
4689
|
+
const shouldRetry = !attempt.policyFailure
|
|
4690
|
+
&& !attempt.reply.isSuccess
|
|
4660
4691
|
&& scenario.shouldRestartIterationOnFail()
|
|
4661
4692
|
&& attempts < maxAttempts
|
|
4662
4693
|
&& !shouldStopNow();
|
|
4663
4694
|
captureAttemptObservation("Bombing", globalOrdinal, attempts - 1, !shouldRetry, attempt, simulationIndex, simulationKind, iterationId, explicitSecondaryOrdinal);
|
|
4695
|
+
if (attempt.policyFailure) {
|
|
4696
|
+
stopScenario = true;
|
|
4697
|
+
scenarioAbortController.abort(attempt.policyFailure);
|
|
4698
|
+
throw attempt.policyFailure;
|
|
4699
|
+
}
|
|
4664
4700
|
if (!shouldRetry) {
|
|
4665
4701
|
for (const step of attempt.recordedSteps) {
|
|
4666
4702
|
recordStepReply(step.stepName, step.reply, step.observedLatencyMs, step.sortIndex);
|
|
@@ -4687,6 +4723,11 @@ async function executeScenarioRuntime(args) {
|
|
|
4687
4723
|
const globalOrdinal = nextObservationOrdinal();
|
|
4688
4724
|
const attempt = await runSingleInvocation("WarmUp", instanceInfo.instanceData, instanceInfo.instanceNumber, instanceInfo.instanceId, false);
|
|
4689
4725
|
captureAttemptObservation("WarmUp", globalOrdinal, 0, true, attempt, -1, "SingleInvocation");
|
|
4726
|
+
if (attempt.policyFailure) {
|
|
4727
|
+
stopScenario = true;
|
|
4728
|
+
scenarioAbortController.abort(attempt.policyFailure);
|
|
4729
|
+
throw attempt.policyFailure;
|
|
4730
|
+
}
|
|
4690
4731
|
}
|
|
4691
4732
|
};
|
|
4692
4733
|
const executeV2TimedConstant = async (copies, durationNs, ramping, runSimulationInvocation, segment) => {
|
|
@@ -5357,10 +5398,17 @@ async function waitForScenarioTasks(tasks, scenarioName, timeoutSeconds, logger,
|
|
|
5357
5398
|
throwPolicyFailure(await settled);
|
|
5358
5399
|
return;
|
|
5359
5400
|
}
|
|
5360
|
-
const
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5401
|
+
const timeoutController = new AbortController();
|
|
5402
|
+
let completed;
|
|
5403
|
+
try {
|
|
5404
|
+
completed = await Promise.race([
|
|
5405
|
+
settled.then(() => true),
|
|
5406
|
+
delayWithAbort(Math.trunc(timeoutSeconds * 1000), combineAbortSignals(signal, timeoutController.signal)).then(() => false)
|
|
5407
|
+
]);
|
|
5408
|
+
}
|
|
5409
|
+
finally {
|
|
5410
|
+
timeoutController.abort();
|
|
5411
|
+
}
|
|
5364
5412
|
if (!completed) {
|
|
5365
5413
|
if (signal.reason instanceof RuntimePolicyCallbackError) {
|
|
5366
5414
|
throw signal.reason;
|
|
@@ -9422,11 +9470,23 @@ function readRuntimeTrackingId(payload, selector) {
|
|
|
9422
9470
|
return null;
|
|
9423
9471
|
}
|
|
9424
9472
|
let current = body;
|
|
9425
|
-
|
|
9473
|
+
const path = selector.slice("json:".length).trim().replace(/^\$\./, "");
|
|
9474
|
+
let segments;
|
|
9475
|
+
try {
|
|
9476
|
+
segments = runtimeSafeJsonPathSegments(path);
|
|
9477
|
+
}
|
|
9478
|
+
catch {
|
|
9479
|
+
return null;
|
|
9480
|
+
}
|
|
9481
|
+
for (const segment of segments) {
|
|
9426
9482
|
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
|
9427
9483
|
return null;
|
|
9428
9484
|
}
|
|
9429
|
-
|
|
9485
|
+
const record = current;
|
|
9486
|
+
if (!Object.prototype.hasOwnProperty.call(record, segment)) {
|
|
9487
|
+
return null;
|
|
9488
|
+
}
|
|
9489
|
+
current = runtimeReadOwnJsonProperty(record, segment);
|
|
9430
9490
|
}
|
|
9431
9491
|
return current == null ? null : String(current);
|
|
9432
9492
|
}
|
|
@@ -9450,23 +9510,57 @@ function runtimeParseBodyAsObject(body) {
|
|
|
9450
9510
|
}
|
|
9451
9511
|
}
|
|
9452
9512
|
function setRuntimeJsonPathValue(body, path, value) {
|
|
9453
|
-
const target =
|
|
9454
|
-
|
|
9455
|
-
: {};
|
|
9456
|
-
const segments = path.split(".").filter(Boolean);
|
|
9513
|
+
const target = runtimeCloneJsonRecord(body);
|
|
9514
|
+
const segments = runtimeSafeJsonPathSegments(path);
|
|
9457
9515
|
if (!segments.length) {
|
|
9458
9516
|
return target;
|
|
9459
9517
|
}
|
|
9460
9518
|
let current = target;
|
|
9461
9519
|
for (let i = 0; i < segments.length - 1; i += 1) {
|
|
9462
9520
|
const segment = segments[i];
|
|
9463
|
-
const next = current
|
|
9521
|
+
const next = runtimeReadOwnJsonProperty(current, segment);
|
|
9522
|
+
let child;
|
|
9464
9523
|
if (!next || typeof next !== "object" || Array.isArray(next)) {
|
|
9465
|
-
|
|
9524
|
+
child = {};
|
|
9525
|
+
}
|
|
9526
|
+
else {
|
|
9527
|
+
child = runtimeCloneJsonRecord(next);
|
|
9466
9528
|
}
|
|
9467
|
-
current
|
|
9529
|
+
runtimeDefineJsonProperty(current, segment, child);
|
|
9530
|
+
current = child;
|
|
9531
|
+
}
|
|
9532
|
+
runtimeDefineJsonProperty(current, segments[segments.length - 1], value);
|
|
9533
|
+
return target;
|
|
9534
|
+
}
|
|
9535
|
+
const FORBIDDEN_RUNTIME_JSON_PATH_SEGMENTS = new Set(["__proto__", "prototype", "constructor"]);
|
|
9536
|
+
function runtimeSafeJsonPathSegments(path) {
|
|
9537
|
+
const segments = path.split(".").filter(Boolean);
|
|
9538
|
+
const forbidden = segments.find((segment) => FORBIDDEN_RUNTIME_JSON_PATH_SEGMENTS.has(segment));
|
|
9539
|
+
if (forbidden) {
|
|
9540
|
+
throw new Error(`Tracking selector contains forbidden JSON path segment '${forbidden}'.`);
|
|
9541
|
+
}
|
|
9542
|
+
return segments;
|
|
9543
|
+
}
|
|
9544
|
+
function runtimeDefineJsonProperty(target, key, value) {
|
|
9545
|
+
Object.defineProperty(target, key, {
|
|
9546
|
+
configurable: true,
|
|
9547
|
+
enumerable: true,
|
|
9548
|
+
value,
|
|
9549
|
+
writable: true
|
|
9550
|
+
});
|
|
9551
|
+
}
|
|
9552
|
+
function runtimeReadOwnJsonProperty(target, key) {
|
|
9553
|
+
const descriptor = Object.getOwnPropertyDescriptor(target, key);
|
|
9554
|
+
return descriptor && "value" in descriptor ? descriptor.value : undefined;
|
|
9555
|
+
}
|
|
9556
|
+
function runtimeCloneJsonRecord(value) {
|
|
9557
|
+
const target = {};
|
|
9558
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
9559
|
+
return target;
|
|
9560
|
+
}
|
|
9561
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
9562
|
+
runtimeDefineJsonProperty(target, key, entry);
|
|
9468
9563
|
}
|
|
9469
|
-
current[segments[segments.length - 1]] = value;
|
|
9470
9564
|
return target;
|
|
9471
9565
|
}
|
|
9472
9566
|
function asTrackingRecord(value) {
|
|
@@ -9476,8 +9570,8 @@ function asTrackingRecord(value) {
|
|
|
9476
9570
|
}
|
|
9477
9571
|
function pickTrackingValue(source, ...keys) {
|
|
9478
9572
|
for (const key of keys) {
|
|
9479
|
-
if (key
|
|
9480
|
-
return source
|
|
9573
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
9574
|
+
return runtimeReadOwnJsonProperty(source, key);
|
|
9481
9575
|
}
|
|
9482
9576
|
}
|
|
9483
9577
|
return undefined;
|
|
@@ -9731,26 +9825,10 @@ function createDefaultLogger(logFilePath) {
|
|
|
9731
9825
|
function wrapLoggerWithMinimumLevel(baseLogger, minimumLogLevel) {
|
|
9732
9826
|
const threshold = logLevelOrder(minimumLogLevel);
|
|
9733
9827
|
return {
|
|
9734
|
-
debug: (message) =>
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9738
|
-
},
|
|
9739
|
-
info: (message) => {
|
|
9740
|
-
if (threshold <= 1) {
|
|
9741
|
-
baseLogger.info(message);
|
|
9742
|
-
}
|
|
9743
|
-
},
|
|
9744
|
-
warn: (message) => {
|
|
9745
|
-
if (threshold <= 2) {
|
|
9746
|
-
baseLogger.warn(message);
|
|
9747
|
-
}
|
|
9748
|
-
},
|
|
9749
|
-
error: (message) => {
|
|
9750
|
-
if (threshold <= 3) {
|
|
9751
|
-
baseLogger.error(message);
|
|
9752
|
-
}
|
|
9753
|
-
}
|
|
9828
|
+
debug: (message) => threshold <= 0 ? baseLogger.debug(message) : undefined,
|
|
9829
|
+
info: (message) => threshold <= 1 ? baseLogger.info(message) : undefined,
|
|
9830
|
+
warn: (message) => threshold <= 2 ? baseLogger.warn(message) : undefined,
|
|
9831
|
+
error: (message) => threshold <= 3 ? baseLogger.error(message) : undefined
|
|
9754
9832
|
};
|
|
9755
9833
|
}
|
|
9756
9834
|
function formatDefaultLoggerLine(level, message) {
|
|
@@ -11264,6 +11342,7 @@ export const __loadstrikeTestExports = {
|
|
|
11264
11342
|
parseStrictBooleanToken,
|
|
11265
11343
|
percentile,
|
|
11266
11344
|
pickOptionalTrackingSelectorString,
|
|
11345
|
+
pickTrackingValue,
|
|
11267
11346
|
pickTrackingNumber,
|
|
11268
11347
|
produceOrConsumeTrackingPayload,
|
|
11269
11348
|
readConfiguredSinkName,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const DEFAULT_SINK_RETRY_COUNT = 3;
|
|
2
|
+
export const DEFAULT_SINK_RETRY_BACKOFF_MS = 250;
|
|
3
|
+
export const MAXIMUM_SINK_RETRY_COUNT = 100;
|
|
4
|
+
export const MAXIMUM_TIMER_DELAY_MS = 2147483647;
|
|
5
|
+
export function normalizeSinkRetryCount(value) {
|
|
6
|
+
const resolved = value === undefined ? DEFAULT_SINK_RETRY_COUNT : value;
|
|
7
|
+
if (!Number.isFinite(resolved) || resolved <= 0) {
|
|
8
|
+
return 0;
|
|
9
|
+
}
|
|
10
|
+
return Math.min(Math.trunc(resolved), MAXIMUM_SINK_RETRY_COUNT);
|
|
11
|
+
}
|
|
12
|
+
export function normalizeSinkRetryBackoffMs(value) {
|
|
13
|
+
const resolved = value === undefined ? DEFAULT_SINK_RETRY_BACKOFF_MS : value;
|
|
14
|
+
if (!Number.isFinite(resolved) || resolved <= 0) {
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
return Math.min(Math.trunc(resolved), MAXIMUM_TIMER_DELAY_MS);
|
|
18
|
+
}
|
|
19
|
+
export function sinkRetryDelayMs(baseDelayMs, retryNumber) {
|
|
20
|
+
if (baseDelayMs <= 0) {
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
let delayMs = Math.min(Math.trunc(baseDelayMs), MAXIMUM_TIMER_DELAY_MS);
|
|
24
|
+
const doublings = Math.max(Math.trunc(retryNumber) - 1, 0);
|
|
25
|
+
for (let index = 0; index < doublings; index += 1) {
|
|
26
|
+
if (delayMs >= Math.ceil(MAXIMUM_TIMER_DELAY_MS / 2)) {
|
|
27
|
+
return MAXIMUM_TIMER_DELAY_MS;
|
|
28
|
+
}
|
|
29
|
+
delayMs *= 2;
|
|
30
|
+
}
|
|
31
|
+
return delayMs;
|
|
32
|
+
}
|
|
33
|
+
export async function waitForSinkRetryDelay(delayMs) {
|
|
34
|
+
if (delayMs <= 0) {
|
|
35
|
+
await cooperativeSinkRetryYield();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
await new Promise((resolve) => {
|
|
39
|
+
setTimeout(resolve, Math.min(delayMs, MAXIMUM_TIMER_DELAY_MS));
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
export async function cooperativeSinkRetryYield() {
|
|
43
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
44
|
+
}
|