@loadstrike/loadstrike-sdk 1.0.22801 → 1.0.23201
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 +1 -1
- package/dist/cjs/autopilot.js +56 -2
- package/dist/cjs/index.js +4 -3
- package/dist/cjs/local.js +158 -156
- package/dist/cjs/runtime.js +53 -1
- package/dist/cjs/sinks.js +123 -19
- package/dist/cjs/transports.js +295 -1
- package/dist/esm/autopilot.js +55 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/local.js +158 -156
- package/dist/esm/runtime.js +53 -1
- package/dist/esm/sinks.js +121 -18
- package/dist/esm/transports.js +294 -0
- package/dist/types/autopilot-contracts.d.ts +2 -0
- package/dist/types/autopilot.d.ts +5 -2
- package/dist/types/contracts.d.ts +13 -0
- package/dist/types/index.d.ts +5 -5
- package/dist/types/local.d.ts +1 -0
- package/dist/types/runtime.d.ts +13 -0
- package/dist/types/sinks.d.ts +37 -4
- package/dist/types/transports.d.ts +97 -2
- package/package.json +2 -7
package/dist/esm/index.js
CHANGED
|
@@ -2,5 +2,5 @@ export { LoadStrikeAutopilot, LoadStrikeAutopilotResult } from "./autopilot.js";
|
|
|
2
2
|
export { LoadStrikeAutopilotReadiness } from "./autopilot-contracts.js";
|
|
3
3
|
export { CrossPlatformScenarioConfigurator, ScenarioTrackingExtensions, LoadStrikeContext, LoadStrikePluginData, LoadStrikePluginDataTable, LoadStrikeNodeType, LoadStrikeReportFormat, LoadStrikeResponse, LoadStrikeLogLevel, LoadStrikeScenarioOperation, LoadStrikeOperationType, LoadStrikeRunner, LoadStrikeScenario, LoadStrikeSimulation, CrossPlatformTrackingConfiguration, LoadStrikeMetric, LoadStrikeCounter, LoadStrikeGauge, LoadStrikeStep, LoadStrikeThreshold } from "./runtime.js";
|
|
4
4
|
export { CorrelationStoreConfiguration, CrossPlatformTrackingRuntime, InMemoryCorrelationStore, RedisCorrelationStoreOptions, RedisCorrelationStore, TrackingPayloadBuilder, TrackingFieldSelector } from "./correlation.js";
|
|
5
|
-
export {
|
|
6
|
-
export { DatadogReportingSink, DatadogReportingSinkOptions, GrafanaLokiReportingSink, GrafanaLokiReportingSinkOptions, InfluxDbReportingSink, InfluxDbReportingSinkOptions, OtelCollectorReportingSink, OtelCollectorReportingSinkOptions, SplunkReportingSink, SplunkReportingSinkOptions, TimescaleDbReportingSink, TimescaleDbReportingSinkOptions } from "./sinks.js";
|
|
5
|
+
export { LOADSTRIKE_TRACE_ID_HEADER, LOADSTRIKE_TRACE_ID_TRACKING_FIELD, TrafficEndpointDefinition, HttpEndpointDefinition, KafkaEndpointDefinition, KafkaSaslOptions, RabbitMqEndpointDefinition, NatsEndpointDefinition, RedisStreamsEndpointDefinition, AzureEventHubsEndpointDefinition, SqsEndpointDefinition, DelegateStreamEndpointDefinition, PushDiffusionEndpointDefinition, HttpOAuth2ClientCredentialsOptions, HttpAuthOptions } from "./transports.js";
|
|
6
|
+
export { DatadogReportingSink, DatadogReportingSinkOptions, GrafanaLokiReportingSink, GrafanaLokiReportingSinkOptions, InfluxDbReportingSink, InfluxDbReportingSinkOptions, OtelCollectorReportingSink, OtelCollectorReportingSinkOptions, PortalReportingSink, SplunkReportingSink, SplunkReportingSinkOptions, TimescaleDbReportingSink, TimescaleDbReportingSinkOptions } from "./sinks.js";
|
package/dist/esm/local.js
CHANGED
|
@@ -5,7 +5,6 @@ import { createHash, createVerify, randomUUID } from "node:crypto";
|
|
|
5
5
|
import { CorrelationStoreConfiguration, CrossPlatformTrackingRuntime, RedisCorrelationStoreOptions, RedisCorrelationStore, TrackingFieldSelector } from "./correlation.js";
|
|
6
6
|
import { EndpointAdapterFactory, LOADSTRIKE_TRACE_ID_TRACKING_FIELD } from "./transports.js";
|
|
7
7
|
const DEFAULT_LICENSING_API_BASE_URL = "https://licensing.loadstrike.com";
|
|
8
|
-
const INTERNAL_BLACKBOX_LICENSING_API_BASE_URL_ENVIRONMENT_VARIABLE = "LOADSTRIKE_INTERNAL_BLACKBOX_API_BASE_URL";
|
|
9
8
|
let developmentLicensingApiBaseUrlOverride;
|
|
10
9
|
const BUILT_IN_WORKER_PLUGIN_NAMES = new Set([
|
|
11
10
|
"loadstrike failed responses",
|
|
@@ -17,7 +16,8 @@ const SINK_FEATURE_BY_KIND = {
|
|
|
17
16
|
grafanaloki: "extensions.reporting_sinks.grafana_loki",
|
|
18
17
|
datadog: "extensions.reporting_sinks.datadog",
|
|
19
18
|
splunk: "extensions.reporting_sinks.splunk",
|
|
20
|
-
otelcollector: "extensions.reporting_sinks.otel_collector"
|
|
19
|
+
otelcollector: "extensions.reporting_sinks.otel_collector",
|
|
20
|
+
portal: "extensions.reporting_sinks.portal"
|
|
21
21
|
};
|
|
22
22
|
const TRACKING_FEATURE_BY_KIND = {
|
|
23
23
|
http: "endpoint.http",
|
|
@@ -27,7 +27,8 @@ const TRACKING_FEATURE_BY_KIND = {
|
|
|
27
27
|
pushdiffusion: "endpoint.push_diffusion",
|
|
28
28
|
delegatestream: "endpoint.delegate_stream",
|
|
29
29
|
nats: "endpoint.nats",
|
|
30
|
-
redisstreams: "endpoint.redis_streams"
|
|
30
|
+
redisstreams: "endpoint.redis_streams",
|
|
31
|
+
sqs: "endpoint.sqs"
|
|
31
32
|
};
|
|
32
33
|
const CI_ENVIRONMENT_VARIABLES = [
|
|
33
34
|
"GITHUB_ACTIONS",
|
|
@@ -57,6 +58,9 @@ export class LoadStrikeLocalClient {
|
|
|
57
58
|
this.licensingApiBaseUrl = resolveLicensingApiBaseUrl();
|
|
58
59
|
this.licenseValidationTimeoutMs = normalizeTimeoutMs(options.licenseValidationTimeoutMs);
|
|
59
60
|
}
|
|
61
|
+
portalReportingIngestUrl() {
|
|
62
|
+
return `${this.licensingApiBaseUrl.replace(/\/+$/, "")}/api/v1/reporting/ingest`;
|
|
63
|
+
}
|
|
60
64
|
async run(request) {
|
|
61
65
|
const sanitized = sanitizeRequest(request);
|
|
62
66
|
const licenseSession = await this.acquireLicenseLease(sanitized);
|
|
@@ -466,25 +470,8 @@ function normalizeLicensingApiBaseUrl(value) {
|
|
|
466
470
|
const normalized = (value ?? "").trim();
|
|
467
471
|
return normalized || DEFAULT_LICENSING_API_BASE_URL;
|
|
468
472
|
}
|
|
469
|
-
function resolveInternalBlackboxLicensingApiBaseUrlOverride(value) {
|
|
470
|
-
const normalized = String(value ?? "").trim();
|
|
471
|
-
if (!normalized) {
|
|
472
|
-
return undefined;
|
|
473
|
-
}
|
|
474
|
-
try {
|
|
475
|
-
const parsed = new URL(normalized);
|
|
476
|
-
const host = parsed.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
477
|
-
if (["http:", "https:"].includes(parsed.protocol) && ["127.0.0.1", "localhost", "::1"].includes(host)) {
|
|
478
|
-
return normalized.replace(/\/+$/, "");
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
catch {
|
|
482
|
-
return undefined;
|
|
483
|
-
}
|
|
484
|
-
return undefined;
|
|
485
|
-
}
|
|
486
473
|
function resolveLicensingApiBaseUrl() {
|
|
487
|
-
return normalizeLicensingApiBaseUrl(
|
|
474
|
+
return normalizeLicensingApiBaseUrl(developmentLicensingApiBaseUrlOverride);
|
|
488
475
|
}
|
|
489
476
|
function setDevelopmentLicensingApiBaseUrlOverride(value) {
|
|
490
477
|
developmentLicensingApiBaseUrlOverride = value;
|
|
@@ -523,6 +510,9 @@ function collectRequestedFeatures(request) {
|
|
|
523
510
|
if (countCustomWorkerPlugins(context) > 0) {
|
|
524
511
|
features.add("extensions.worker_plugins.custom");
|
|
525
512
|
}
|
|
513
|
+
if (asList(pickValue(context, "RuntimePolicies", "runtimePolicies")).length > 0) {
|
|
514
|
+
features.add("policy.runtime_controls");
|
|
515
|
+
}
|
|
526
516
|
const reportingSinks = asList(pickValue(context, "ReportingSinks", "reportingSinks"));
|
|
527
517
|
let hasCustomSink = false;
|
|
528
518
|
for (const sink of reportingSinks) {
|
|
@@ -818,159 +808,171 @@ async function evaluateScenarioOutcome(scenario, requestCount, context) {
|
|
|
818
808
|
validateTrackingConfiguration(tracking, sourceEndpoint, destinationEndpoint);
|
|
819
809
|
const sourceAdapter = EndpointAdapterFactory.create(sourceEndpoint);
|
|
820
810
|
const destinationAdapter = destinationEndpoint ? EndpointAdapterFactory.create(destinationEndpoint) : null;
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
let
|
|
870
|
-
let attempts = 0;
|
|
871
|
-
const maxAttempts = 1 + (restartOnFail ? restartMaxAttempts : 0);
|
|
872
|
-
while (!iterationComplete) {
|
|
873
|
-
attempts += 1;
|
|
811
|
+
let correlationStore = null;
|
|
812
|
+
try {
|
|
813
|
+
await sourceAdapter.initialize?.();
|
|
814
|
+
await destinationAdapter?.initialize?.();
|
|
815
|
+
const correlationTimeoutOverride = pickValue(tracking, "CorrelationTimeoutMs", "correlationTimeoutMs");
|
|
816
|
+
const correlationTimeoutSeconds = pickValue(tracking, "CorrelationTimeoutSeconds", "correlationTimeoutSeconds", "CorrelationTimeout");
|
|
817
|
+
const timeoutMs = correlationTimeoutOverride != null && String(correlationTimeoutOverride).trim() !== ""
|
|
818
|
+
? asInt(correlationTimeoutOverride)
|
|
819
|
+
: Math.trunc((correlationTimeoutSeconds == null || String(correlationTimeoutSeconds).trim() === ""
|
|
820
|
+
? 30
|
|
821
|
+
: asNumber(correlationTimeoutSeconds)) * 1000);
|
|
822
|
+
const timeoutCountsAsFailure = toBoolean(pickValue(tracking, "TimeoutCountsAsFailure", "timeoutCountsAsFailure"), true);
|
|
823
|
+
correlationStore = mapCorrelationStore(tracking, buildTrackingRunNamespace(stringOrDefault(pickValue(context, "SessionId", "sessionId"), "session"), stringOrDefault(pickValue(scenario, "Name", "name"), "scenario"), sourceEndpoint.name, destinationEndpoint?.name));
|
|
824
|
+
const timeoutSweepIntervalOverride = pickValue(tracking, "TimeoutSweepIntervalMs", "timeoutSweepIntervalMs");
|
|
825
|
+
const timeoutSweepIntervalSeconds = pickValue(tracking, "TimeoutSweepIntervalSeconds", "timeoutSweepIntervalSeconds");
|
|
826
|
+
const timeoutSweepIntervalMs = timeoutSweepIntervalOverride != null && String(timeoutSweepIntervalOverride).trim() !== ""
|
|
827
|
+
? asInt(timeoutSweepIntervalOverride)
|
|
828
|
+
: Math.trunc((timeoutSweepIntervalSeconds == null || String(timeoutSweepIntervalSeconds).trim() === ""
|
|
829
|
+
? 1
|
|
830
|
+
: asNumber(timeoutSweepIntervalSeconds)) * 1000);
|
|
831
|
+
const timeoutBatchSizeValue = pickValue(tracking, "TimeoutBatchSize", "timeoutBatchSize");
|
|
832
|
+
const timeoutBatchSize = timeoutBatchSizeValue == null || String(timeoutBatchSizeValue).trim() === ""
|
|
833
|
+
? 200
|
|
834
|
+
: asInt(timeoutBatchSizeValue);
|
|
835
|
+
const runtime = new CrossPlatformTrackingRuntime({
|
|
836
|
+
sourceTrackingField: sourceEndpoint.trackingField,
|
|
837
|
+
destinationTrackingField: destinationEndpoint?.trackingField,
|
|
838
|
+
destinationGatherByField: destinationEndpoint?.gatherByField,
|
|
839
|
+
trackingFieldValueCaseSensitive: toBoolean(pickValue(tracking, "TrackingFieldValueCaseSensitive", "trackingFieldValueCaseSensitive"), true),
|
|
840
|
+
gatherByFieldValueCaseSensitive: toBoolean(pickValue(tracking, "GatherByFieldValueCaseSensitive", "gatherByFieldValueCaseSensitive"), true),
|
|
841
|
+
correlationTimeoutMs: timeoutMs,
|
|
842
|
+
timeoutCountsAsFailure,
|
|
843
|
+
store: correlationStore ?? undefined
|
|
844
|
+
});
|
|
845
|
+
const runMode = stringOrDefault(pickValue(tracking, "RunMode", "runMode"), "GenerateAndCorrelate").trim().toLowerCase();
|
|
846
|
+
const sourceOnlyMode = !destinationEndpoint;
|
|
847
|
+
const restartOnFail = toBoolean(pickValue(scenario, "RestartIterationOnFail", "restartIterationOnFail"), false);
|
|
848
|
+
const restartMaxAttempts = Math.max(asInt(pickValue(context, "RestartIterationMaxAttempts", "restartIterationMaxAttempts")), 0);
|
|
849
|
+
const maxFailCount = Math.max(asInt(pickValue(scenario, "MaxFailCount", "maxFailCount")), 0);
|
|
850
|
+
const scenarioCompletionTimeoutSeconds = Math.max(asNumber(pickValue(context, "ScenarioCompletionTimeoutSeconds", "scenarioCompletionTimeoutSeconds")), 0);
|
|
851
|
+
const scenarioDeadlineMs = scenarioCompletionTimeoutSeconds > 0
|
|
852
|
+
? Date.now() + Math.trunc(scenarioCompletionTimeoutSeconds * 1000)
|
|
853
|
+
: Number.POSITIVE_INFINITY;
|
|
854
|
+
let okCount = 0;
|
|
855
|
+
let failCount = 0;
|
|
856
|
+
let nowMs = Date.now();
|
|
857
|
+
let processedIterations = 0;
|
|
858
|
+
let lastSweepAtMs = nowMs;
|
|
859
|
+
for (let i = 0; i < requestCount; i += 1) {
|
|
874
860
|
if (Date.now() > scenarioDeadlineMs) {
|
|
875
|
-
iterationComplete = true;
|
|
876
861
|
break;
|
|
877
862
|
}
|
|
878
|
-
let
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
863
|
+
let iterationComplete = false;
|
|
864
|
+
let attempts = 0;
|
|
865
|
+
const maxAttempts = 1 + (restartOnFail ? restartMaxAttempts : 0);
|
|
866
|
+
while (!iterationComplete) {
|
|
867
|
+
attempts += 1;
|
|
868
|
+
if (Date.now() > scenarioDeadlineMs) {
|
|
869
|
+
iterationComplete = true;
|
|
870
|
+
break;
|
|
871
|
+
}
|
|
872
|
+
let sourcePayload = runMode === "correlateexistingtraffic"
|
|
873
|
+
? await sourceAdapter.consume()
|
|
874
|
+
: await sourceAdapter.produce();
|
|
875
|
+
sourcePayload = normalizePayload(sourcePayload, sourceEndpoint, i);
|
|
876
|
+
const sourceTrackingId = sourceOnlyMode
|
|
877
|
+
? readTrackingId(sourcePayload, sourceEndpoint.trackingField)
|
|
878
|
+
: await runtime.onSourceProduced(sourcePayload, nowMs);
|
|
879
|
+
if (!sourceTrackingId) {
|
|
880
|
+
if (restartOnFail && attempts < maxAttempts) {
|
|
881
|
+
nowMs += 1;
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
failCount += 1;
|
|
885
|
+
iterationComplete = true;
|
|
887
886
|
nowMs += 1;
|
|
888
|
-
|
|
887
|
+
break;
|
|
889
888
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
}
|
|
895
|
-
if (sourceOnlyMode || !destinationAdapter || !destinationEndpoint) {
|
|
896
|
-
okCount += 1;
|
|
897
|
-
iterationComplete = true;
|
|
898
|
-
nowMs += 1;
|
|
899
|
-
break;
|
|
900
|
-
}
|
|
901
|
-
let destinationPayload = await destinationAdapter.consume();
|
|
902
|
-
destinationPayload = normalizePayload(destinationPayload, destinationEndpoint, i);
|
|
903
|
-
let matched = await runtime.onDestinationConsumed(destinationPayload, nowMs + 1);
|
|
904
|
-
const correlationDeadlineMs = nowMs + timeoutMs;
|
|
905
|
-
while (!matched && Date.now() <= scenarioDeadlineMs && nowMs < correlationDeadlineMs) {
|
|
906
|
-
const remainingTimeoutMs = correlationDeadlineMs - nowMs;
|
|
907
|
-
if (remainingTimeoutMs <= 0) {
|
|
889
|
+
if (sourceOnlyMode || !destinationAdapter || !destinationEndpoint) {
|
|
890
|
+
okCount += 1;
|
|
891
|
+
iterationComplete = true;
|
|
892
|
+
nowMs += 1;
|
|
908
893
|
break;
|
|
909
894
|
}
|
|
910
|
-
await
|
|
911
|
-
destinationPayload = normalizePayload(
|
|
912
|
-
matched = await runtime.onDestinationConsumed(destinationPayload, nowMs + 1);
|
|
895
|
+
let destinationPayload = await destinationAdapter.consume();
|
|
896
|
+
destinationPayload = normalizePayload(destinationPayload, destinationEndpoint, i);
|
|
897
|
+
let matched = await runtime.onDestinationConsumed(destinationPayload, nowMs + 1);
|
|
898
|
+
const correlationDeadlineMs = nowMs + timeoutMs;
|
|
899
|
+
while (!matched && Date.now() <= scenarioDeadlineMs && nowMs < correlationDeadlineMs) {
|
|
900
|
+
const remainingTimeoutMs = correlationDeadlineMs - nowMs;
|
|
901
|
+
if (remainingTimeoutMs <= 0) {
|
|
902
|
+
break;
|
|
903
|
+
}
|
|
904
|
+
await sleep(Math.min(destinationEndpoint.pollIntervalMs ?? 250, remainingTimeoutMs));
|
|
905
|
+
destinationPayload = normalizePayload(await destinationAdapter.consume(), destinationEndpoint, i);
|
|
906
|
+
matched = await runtime.onDestinationConsumed(destinationPayload, nowMs + 1);
|
|
907
|
+
if (matched) {
|
|
908
|
+
break;
|
|
909
|
+
}
|
|
910
|
+
nowMs += Math.max(destinationEndpoint.pollIntervalMs ?? 250, 1);
|
|
911
|
+
}
|
|
913
912
|
if (matched) {
|
|
914
|
-
|
|
913
|
+
okCount += 1;
|
|
914
|
+
iterationComplete = true;
|
|
915
915
|
}
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
okCount += 1;
|
|
920
|
-
iterationComplete = true;
|
|
921
|
-
}
|
|
922
|
-
else if (restartOnFail && attempts < maxAttempts) {
|
|
923
|
-
nowMs += 2;
|
|
924
|
-
continue;
|
|
925
|
-
}
|
|
926
|
-
else {
|
|
927
|
-
await runtime.sweepTimeouts(nowMs + timeoutMs + 1, timeoutBatchSize || undefined);
|
|
928
|
-
failCount += 1;
|
|
929
|
-
iterationComplete = true;
|
|
930
|
-
}
|
|
931
|
-
nowMs += 2;
|
|
932
|
-
}
|
|
933
|
-
processedIterations += 1;
|
|
934
|
-
if (timeoutSweepIntervalMs > 0 && nowMs - lastSweepAtMs >= timeoutSweepIntervalMs) {
|
|
935
|
-
const swept = await runtime.sweepTimeouts(nowMs, timeoutBatchSize || undefined);
|
|
936
|
-
if (swept > 0) {
|
|
937
|
-
if (timeoutCountsAsFailure) {
|
|
938
|
-
failCount += swept;
|
|
916
|
+
else if (restartOnFail && attempts < maxAttempts) {
|
|
917
|
+
nowMs += 2;
|
|
918
|
+
continue;
|
|
939
919
|
}
|
|
940
920
|
else {
|
|
941
|
-
|
|
921
|
+
await runtime.sweepTimeouts(nowMs + timeoutMs + 1, timeoutBatchSize || undefined);
|
|
922
|
+
failCount += 1;
|
|
923
|
+
iterationComplete = true;
|
|
924
|
+
}
|
|
925
|
+
nowMs += 2;
|
|
926
|
+
}
|
|
927
|
+
processedIterations += 1;
|
|
928
|
+
if (timeoutSweepIntervalMs > 0 && nowMs - lastSweepAtMs >= timeoutSweepIntervalMs) {
|
|
929
|
+
const swept = await runtime.sweepTimeouts(nowMs, timeoutBatchSize || undefined);
|
|
930
|
+
if (swept > 0) {
|
|
931
|
+
if (timeoutCountsAsFailure) {
|
|
932
|
+
failCount += swept;
|
|
933
|
+
}
|
|
934
|
+
else {
|
|
935
|
+
okCount += swept;
|
|
936
|
+
}
|
|
942
937
|
}
|
|
938
|
+
lastSweepAtMs = nowMs;
|
|
939
|
+
}
|
|
940
|
+
if (maxFailCount > 0 && failCount >= maxFailCount) {
|
|
941
|
+
break;
|
|
943
942
|
}
|
|
944
|
-
lastSweepAtMs = nowMs;
|
|
945
|
-
}
|
|
946
|
-
if (maxFailCount > 0 && failCount >= maxFailCount) {
|
|
947
|
-
break;
|
|
948
943
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
944
|
+
let expired = 0;
|
|
945
|
+
let sweptChunk = 0;
|
|
946
|
+
do {
|
|
947
|
+
sweptChunk = await runtime.sweepTimeouts(nowMs + timeoutMs + 1, timeoutBatchSize || undefined);
|
|
948
|
+
expired += sweptChunk;
|
|
949
|
+
} while (timeoutBatchSize > 0 && sweptChunk >= timeoutBatchSize);
|
|
950
|
+
if (expired > 0) {
|
|
951
|
+
if (timeoutCountsAsFailure) {
|
|
952
|
+
failCount += expired;
|
|
953
|
+
}
|
|
954
|
+
else {
|
|
955
|
+
okCount += expired;
|
|
956
|
+
}
|
|
959
957
|
}
|
|
960
|
-
|
|
961
|
-
|
|
958
|
+
if (processedIterations < requestCount && Date.now() > scenarioDeadlineMs) {
|
|
959
|
+
const timedOutIterations = requestCount - processedIterations;
|
|
960
|
+
if (timeoutCountsAsFailure) {
|
|
961
|
+
failCount += timedOutIterations;
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
okCount += timedOutIterations;
|
|
965
|
+
}
|
|
962
966
|
}
|
|
967
|
+
return { requestCount: processedIterations, okCount, failCount };
|
|
963
968
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
okCount += timedOutIterations;
|
|
971
|
-
}
|
|
969
|
+
finally {
|
|
970
|
+
await Promise.allSettled([
|
|
971
|
+
sourceAdapter.dispose?.(),
|
|
972
|
+
destinationAdapter?.dispose?.(),
|
|
973
|
+
correlationStore?.close?.()
|
|
974
|
+
].filter((value) => Boolean(value)));
|
|
972
975
|
}
|
|
973
|
-
return { requestCount: processedIterations, okCount, failCount };
|
|
974
976
|
}
|
|
975
977
|
function validateTrackingConfiguration(tracking, sourceEndpoint, destinationEndpoint) {
|
|
976
978
|
if (sourceEndpoint.gatherByField?.trim()) {
|
package/dist/esm/runtime.js
CHANGED
|
@@ -7,6 +7,7 @@ import { DistributedClusterAgent, DistributedClusterCoordinator } from "./cluste
|
|
|
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 { PortalReportingSink } from "./sinks.js";
|
|
10
11
|
export const LoadStrikeNodeType = {
|
|
11
12
|
SingleNode: "SingleNode",
|
|
12
13
|
Coordinator: "Coordinator",
|
|
@@ -1378,6 +1379,16 @@ export class LoadStrikeContext {
|
|
|
1378
1379
|
validateNamedReportingSinks(sinks);
|
|
1379
1380
|
return this.mergeValues({ ReportingSinks: sinks });
|
|
1380
1381
|
}
|
|
1382
|
+
withPortalReporting() {
|
|
1383
|
+
return this.WithPortalReporting();
|
|
1384
|
+
}
|
|
1385
|
+
WithPortalReporting() {
|
|
1386
|
+
const sinks = [...(this.values.ReportingSinks ?? [])];
|
|
1387
|
+
if (!sinks.some((sink, index) => resolveSinkName(sink, index).trim().toLowerCase() === "portal")) {
|
|
1388
|
+
sinks.push(new PortalReportingSink());
|
|
1389
|
+
}
|
|
1390
|
+
return this.mergeValues({ ReportingSinks: sinks });
|
|
1391
|
+
}
|
|
1381
1392
|
/**
|
|
1382
1393
|
* Registers runtime policies for the run.
|
|
1383
1394
|
* Use this when scenario selection or step execution should obey policy callbacks.
|
|
@@ -2118,6 +2129,9 @@ export class LoadStrikeRunner {
|
|
|
2118
2129
|
static WithReportingSinks(context, ...sinks) {
|
|
2119
2130
|
return context.WithReportingSinks(...sinks);
|
|
2120
2131
|
}
|
|
2132
|
+
static WithPortalReporting(context) {
|
|
2133
|
+
return context.WithPortalReporting();
|
|
2134
|
+
}
|
|
2121
2135
|
/**
|
|
2122
2136
|
* Registers runtime policies for the run.
|
|
2123
2137
|
* Use this when scenario selection or step execution should obey policy callbacks.
|
|
@@ -2346,6 +2360,29 @@ export class LoadStrikeRunner {
|
|
|
2346
2360
|
WithReportingInterval(intervalSeconds) {
|
|
2347
2361
|
return this.withReportingInterval(intervalSeconds);
|
|
2348
2362
|
}
|
|
2363
|
+
withReportingSinks(...sinks) {
|
|
2364
|
+
if (!sinks.length) {
|
|
2365
|
+
throw new Error("At least one reporting sink should be provided.");
|
|
2366
|
+
}
|
|
2367
|
+
if (sinks.some((sink) => sink == null)) {
|
|
2368
|
+
throw new Error("Reporting sink collection cannot contain null values.");
|
|
2369
|
+
}
|
|
2370
|
+
validateNamedReportingSinks(sinks);
|
|
2371
|
+
return this.configure({ reportingSinks: sinks });
|
|
2372
|
+
}
|
|
2373
|
+
WithReportingSinks(...sinks) {
|
|
2374
|
+
return this.withReportingSinks(...sinks);
|
|
2375
|
+
}
|
|
2376
|
+
withPortalReporting() {
|
|
2377
|
+
const sinks = [...(this.options.reportingSinks ?? [])];
|
|
2378
|
+
if (!sinks.some((sink, index) => resolveSinkName(sink, index).trim().toLowerCase() === "portal")) {
|
|
2379
|
+
sinks.push(new PortalReportingSink());
|
|
2380
|
+
}
|
|
2381
|
+
return this.configure({ reportingSinks: sinks });
|
|
2382
|
+
}
|
|
2383
|
+
WithPortalReporting() {
|
|
2384
|
+
return this.withPortalReporting();
|
|
2385
|
+
}
|
|
2349
2386
|
/**
|
|
2350
2387
|
* Sets the timeout for cluster command round-trips.
|
|
2351
2388
|
* Use this when distributed control messages need a tighter or looser deadline.
|
|
@@ -2519,6 +2556,7 @@ export class LoadStrikeRunner {
|
|
|
2519
2556
|
testInfo,
|
|
2520
2557
|
getNodeInfo: () => attachNodeInfoAliases({ ...nodeInfo })
|
|
2521
2558
|
};
|
|
2559
|
+
attachPortalReportingSession(sinkSession, sessionInfo, licenseClient, licenseSession);
|
|
2522
2560
|
attachSessionStartInfoAliases(sessionInfo);
|
|
2523
2561
|
nodeInfo.currentOperation = "Init";
|
|
2524
2562
|
for (const plugin of plugins) {
|
|
@@ -4052,10 +4090,23 @@ function attachSessionStartInfoAliases(session) {
|
|
|
4052
4090
|
attachAliasMap(session, {
|
|
4053
4091
|
StartedUtc: "startedUtc",
|
|
4054
4092
|
ScenarioNames: "scenarioNames",
|
|
4055
|
-
Scenarios: "scenarios"
|
|
4093
|
+
Scenarios: "scenarios",
|
|
4094
|
+
RunToken: "runToken",
|
|
4095
|
+
PortalReportingIngestUrl: "portalReportingIngestUrl"
|
|
4056
4096
|
});
|
|
4057
4097
|
return session;
|
|
4058
4098
|
}
|
|
4099
|
+
function attachPortalReportingSession(sinkSession, sessionInfo, licenseClient, licenseSession) {
|
|
4100
|
+
const runToken = stringValueOrDefault(licenseSession?.runToken, "").trim();
|
|
4101
|
+
if (!runToken || !licenseClient) {
|
|
4102
|
+
return;
|
|
4103
|
+
}
|
|
4104
|
+
const ingestUrl = licenseClient.portalReportingIngestUrl();
|
|
4105
|
+
sinkSession.runToken = runToken;
|
|
4106
|
+
sinkSession.portalReportingIngestUrl = ingestUrl;
|
|
4107
|
+
sessionInfo.runToken = runToken;
|
|
4108
|
+
sessionInfo.portalReportingIngestUrl = ingestUrl;
|
|
4109
|
+
}
|
|
4059
4110
|
function attachScenarioInitContextAliases(context) {
|
|
4060
4111
|
context.nodeInfo = attachNodeInfoAliases(context.nodeInfo);
|
|
4061
4112
|
context.testInfo = attachTestInfoAliases(context.testInfo);
|
|
@@ -6606,6 +6657,7 @@ function mapRuntimeTrackingEndpointSpec(spec, useLoadStrikeTraceIdHeader = false
|
|
|
6606
6657
|
nats: asTrackingRecord(pickTrackingValue(spec, "Nats", "nats")),
|
|
6607
6658
|
redisStreams: asTrackingRecord(pickTrackingValue(spec, "RedisStreams", "redisStreams")),
|
|
6608
6659
|
azureEventHubs: asTrackingRecord(pickTrackingValue(spec, "AzureEventHubs", "azureEventHubs")),
|
|
6660
|
+
sqs: asTrackingRecord(pickTrackingValue(spec, "Sqs", "sqs")),
|
|
6609
6661
|
pushDiffusion: asTrackingRecord(pickTrackingValue(spec, "PushDiffusion", "pushDiffusion")),
|
|
6610
6662
|
delegate: typeof delegateProduce === "function"
|
|
6611
6663
|
|| typeof delegateConsume === "function"
|