@juspay/neurolink 9.24.0 → 9.25.0
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/CHANGELOG.md +6 -0
- package/dist/adapters/tts/googleTTSHandler.js +26 -1
- package/dist/adapters/video/vertexVideoHandler.js +23 -17
- package/dist/cli/commands/config.d.ts +3 -3
- package/dist/cli/commands/observability.d.ts +53 -0
- package/dist/cli/commands/observability.js +453 -0
- package/dist/cli/commands/telemetry.d.ts +63 -0
- package/dist/cli/commands/telemetry.js +689 -0
- package/dist/cli/factories/commandFactory.js +29 -15
- package/dist/cli/parser.js +6 -9
- package/dist/cli/utils/formatters.d.ts +13 -0
- package/dist/cli/utils/formatters.js +23 -0
- package/dist/constants/contextWindows.js +6 -0
- package/dist/constants/enums.d.ts +6 -0
- package/dist/constants/enums.js +8 -2
- package/dist/context/budgetChecker.js +75 -48
- package/dist/context/contextCompactor.js +135 -127
- package/dist/core/baseProvider.d.ts +5 -0
- package/dist/core/baseProvider.js +158 -102
- package/dist/core/conversationMemoryInitializer.js +7 -4
- package/dist/core/conversationMemoryManager.d.ts +2 -0
- package/dist/core/conversationMemoryManager.js +6 -2
- package/dist/core/modules/GenerationHandler.d.ts +2 -2
- package/dist/core/modules/GenerationHandler.js +12 -12
- package/dist/evaluation/ragasEvaluator.js +39 -19
- package/dist/evaluation/scoring.js +46 -20
- package/dist/features/ppt/presentationOrchestrator.js +23 -0
- package/dist/features/ppt/slideGenerator.js +13 -0
- package/dist/features/ppt/slideRenderers.d.ts +1 -1
- package/dist/features/ppt/slideRenderers.js +6 -4
- package/dist/features/ppt/slideTypeInference.d.ts +1 -1
- package/dist/features/ppt/slideTypeInference.js +75 -73
- package/dist/files/fileTools.d.ts +6 -6
- package/dist/index.d.ts +46 -12
- package/dist/index.js +79 -17
- package/dist/lib/adapters/tts/googleTTSHandler.js +26 -1
- package/dist/lib/adapters/video/vertexVideoHandler.js +23 -17
- package/dist/lib/constants/contextWindows.js +6 -0
- package/dist/lib/constants/enums.d.ts +6 -0
- package/dist/lib/constants/enums.js +8 -2
- package/dist/lib/context/budgetChecker.js +75 -48
- package/dist/lib/context/contextCompactor.js +135 -127
- package/dist/lib/core/baseProvider.d.ts +5 -0
- package/dist/lib/core/baseProvider.js +158 -102
- package/dist/lib/core/conversationMemoryInitializer.js +7 -4
- package/dist/lib/core/conversationMemoryManager.d.ts +2 -0
- package/dist/lib/core/conversationMemoryManager.js +6 -2
- package/dist/lib/core/modules/GenerationHandler.d.ts +2 -2
- package/dist/lib/core/modules/GenerationHandler.js +12 -12
- package/dist/lib/evaluation/ragasEvaluator.js +39 -19
- package/dist/lib/evaluation/scoring.js +46 -20
- package/dist/lib/features/ppt/presentationOrchestrator.js +23 -0
- package/dist/lib/features/ppt/slideGenerator.js +13 -0
- package/dist/lib/features/ppt/slideRenderers.d.ts +1 -1
- package/dist/lib/features/ppt/slideRenderers.js +6 -4
- package/dist/lib/features/ppt/slideTypeInference.d.ts +1 -1
- package/dist/lib/features/ppt/slideTypeInference.js +75 -73
- package/dist/lib/files/fileTools.d.ts +6 -6
- package/dist/lib/index.d.ts +46 -12
- package/dist/lib/index.js +79 -17
- package/dist/lib/mcp/httpRateLimiter.js +39 -12
- package/dist/lib/mcp/httpRetryHandler.js +22 -1
- package/dist/lib/mcp/mcpClientFactory.js +13 -15
- package/dist/lib/memory/memoryRetrievalTools.js +22 -0
- package/dist/lib/neurolink.d.ts +64 -72
- package/dist/lib/neurolink.js +984 -566
- package/dist/lib/observability/exporterRegistry.d.ts +152 -0
- package/dist/lib/observability/exporterRegistry.js +414 -0
- package/dist/lib/observability/exporters/arizeExporter.d.ts +32 -0
- package/dist/lib/observability/exporters/arizeExporter.js +139 -0
- package/dist/lib/observability/exporters/baseExporter.d.ts +117 -0
- package/dist/lib/observability/exporters/baseExporter.js +191 -0
- package/dist/lib/observability/exporters/braintrustExporter.d.ts +30 -0
- package/dist/lib/observability/exporters/braintrustExporter.js +155 -0
- package/dist/lib/observability/exporters/datadogExporter.d.ts +37 -0
- package/dist/lib/observability/exporters/datadogExporter.js +197 -0
- package/dist/lib/observability/exporters/index.d.ts +13 -0
- package/dist/lib/observability/exporters/index.js +14 -0
- package/dist/lib/observability/exporters/laminarExporter.d.ts +48 -0
- package/dist/lib/observability/exporters/laminarExporter.js +303 -0
- package/dist/lib/observability/exporters/langfuseExporter.d.ts +47 -0
- package/dist/lib/observability/exporters/langfuseExporter.js +200 -0
- package/dist/lib/observability/exporters/langsmithExporter.d.ts +26 -0
- package/dist/lib/observability/exporters/langsmithExporter.js +124 -0
- package/dist/lib/observability/exporters/otelExporter.d.ts +39 -0
- package/dist/lib/observability/exporters/otelExporter.js +165 -0
- package/dist/lib/observability/exporters/posthogExporter.d.ts +48 -0
- package/dist/lib/observability/exporters/posthogExporter.js +288 -0
- package/dist/lib/observability/exporters/sentryExporter.d.ts +32 -0
- package/dist/lib/observability/exporters/sentryExporter.js +166 -0
- package/dist/lib/observability/index.d.ts +25 -0
- package/dist/lib/observability/index.js +32 -0
- package/dist/lib/observability/metricsAggregator.d.ts +260 -0
- package/dist/lib/observability/metricsAggregator.js +553 -0
- package/dist/lib/observability/otelBridge.d.ts +49 -0
- package/dist/lib/observability/otelBridge.js +132 -0
- package/dist/lib/observability/retryPolicy.d.ts +192 -0
- package/dist/lib/observability/retryPolicy.js +384 -0
- package/dist/lib/observability/sampling/index.d.ts +4 -0
- package/dist/lib/observability/sampling/index.js +5 -0
- package/dist/lib/observability/sampling/samplers.d.ts +116 -0
- package/dist/lib/observability/sampling/samplers.js +217 -0
- package/dist/lib/observability/spanProcessor.d.ts +129 -0
- package/dist/lib/observability/spanProcessor.js +288 -0
- package/dist/lib/observability/tokenTracker.d.ts +156 -0
- package/dist/lib/observability/tokenTracker.js +414 -0
- package/dist/lib/observability/types/exporterTypes.d.ts +250 -0
- package/dist/lib/observability/types/exporterTypes.js +6 -0
- package/dist/lib/observability/types/index.d.ts +6 -0
- package/dist/lib/observability/types/index.js +5 -0
- package/dist/lib/observability/types/spanTypes.d.ts +244 -0
- package/dist/lib/observability/types/spanTypes.js +93 -0
- package/dist/lib/observability/utils/index.d.ts +4 -0
- package/dist/lib/observability/utils/index.js +5 -0
- package/dist/lib/observability/utils/spanSerializer.d.ts +115 -0
- package/dist/lib/observability/utils/spanSerializer.js +287 -0
- package/dist/lib/providers/amazonSagemaker.d.ts +5 -4
- package/dist/lib/providers/amazonSagemaker.js +3 -4
- package/dist/lib/providers/googleVertex.d.ts +7 -0
- package/dist/lib/providers/googleVertex.js +80 -2
- package/dist/lib/rag/pipeline/RAGPipeline.d.ts +0 -5
- package/dist/lib/rag/pipeline/RAGPipeline.js +122 -87
- package/dist/lib/rag/ragIntegration.js +30 -0
- package/dist/lib/rag/retrieval/hybridSearch.js +22 -0
- package/dist/lib/server/abstract/baseServerAdapter.js +51 -19
- package/dist/lib/server/middleware/common.js +44 -12
- package/dist/lib/services/server/ai/observability/instrumentation.d.ts +2 -2
- package/dist/lib/services/server/ai/observability/instrumentation.js +10 -5
- package/dist/lib/types/conversationMemoryInterface.d.ts +2 -0
- package/dist/lib/types/modelTypes.d.ts +18 -18
- package/dist/lib/types/providers.d.ts +5 -0
- package/dist/lib/utils/pricing.js +25 -1
- package/dist/lib/utils/ttsProcessor.js +74 -59
- package/dist/lib/workflow/config.d.ts +36 -36
- package/dist/lib/workflow/core/ensembleExecutor.js +10 -0
- package/dist/lib/workflow/core/judgeScorer.js +20 -2
- package/dist/lib/workflow/core/workflowRunner.js +34 -1
- package/dist/mcp/httpRateLimiter.js +39 -12
- package/dist/mcp/httpRetryHandler.js +22 -1
- package/dist/mcp/mcpClientFactory.js +13 -15
- package/dist/memory/memoryRetrievalTools.js +22 -0
- package/dist/neurolink.d.ts +64 -72
- package/dist/neurolink.js +984 -566
- package/dist/observability/FEATURE-STATUS.md +269 -0
- package/dist/observability/exporterRegistry.d.ts +152 -0
- package/dist/observability/exporterRegistry.js +413 -0
- package/dist/observability/exporters/arizeExporter.d.ts +32 -0
- package/dist/observability/exporters/arizeExporter.js +138 -0
- package/dist/observability/exporters/baseExporter.d.ts +117 -0
- package/dist/observability/exporters/baseExporter.js +190 -0
- package/dist/observability/exporters/braintrustExporter.d.ts +30 -0
- package/dist/observability/exporters/braintrustExporter.js +154 -0
- package/dist/observability/exporters/datadogExporter.d.ts +37 -0
- package/dist/observability/exporters/datadogExporter.js +196 -0
- package/dist/observability/exporters/index.d.ts +13 -0
- package/dist/observability/exporters/index.js +13 -0
- package/dist/observability/exporters/laminarExporter.d.ts +48 -0
- package/dist/observability/exporters/laminarExporter.js +302 -0
- package/dist/observability/exporters/langfuseExporter.d.ts +47 -0
- package/dist/observability/exporters/langfuseExporter.js +199 -0
- package/dist/observability/exporters/langsmithExporter.d.ts +26 -0
- package/dist/observability/exporters/langsmithExporter.js +123 -0
- package/dist/observability/exporters/otelExporter.d.ts +39 -0
- package/dist/observability/exporters/otelExporter.js +164 -0
- package/dist/observability/exporters/posthogExporter.d.ts +48 -0
- package/dist/observability/exporters/posthogExporter.js +287 -0
- package/dist/observability/exporters/sentryExporter.d.ts +32 -0
- package/dist/observability/exporters/sentryExporter.js +165 -0
- package/dist/observability/index.d.ts +25 -0
- package/dist/observability/index.js +31 -0
- package/dist/observability/metricsAggregator.d.ts +260 -0
- package/dist/observability/metricsAggregator.js +552 -0
- package/dist/observability/otelBridge.d.ts +49 -0
- package/dist/observability/otelBridge.js +131 -0
- package/dist/observability/retryPolicy.d.ts +192 -0
- package/dist/observability/retryPolicy.js +383 -0
- package/dist/observability/sampling/index.d.ts +4 -0
- package/dist/observability/sampling/index.js +4 -0
- package/dist/observability/sampling/samplers.d.ts +116 -0
- package/dist/observability/sampling/samplers.js +216 -0
- package/dist/observability/spanProcessor.d.ts +129 -0
- package/dist/observability/spanProcessor.js +287 -0
- package/dist/observability/tokenTracker.d.ts +156 -0
- package/dist/observability/tokenTracker.js +413 -0
- package/dist/observability/types/exporterTypes.d.ts +250 -0
- package/dist/observability/types/exporterTypes.js +5 -0
- package/dist/observability/types/index.d.ts +6 -0
- package/dist/observability/types/index.js +4 -0
- package/dist/observability/types/spanTypes.d.ts +244 -0
- package/dist/observability/types/spanTypes.js +92 -0
- package/dist/observability/utils/index.d.ts +4 -0
- package/dist/observability/utils/index.js +4 -0
- package/dist/observability/utils/spanSerializer.d.ts +115 -0
- package/dist/observability/utils/spanSerializer.js +286 -0
- package/dist/providers/amazonSagemaker.d.ts +5 -4
- package/dist/providers/amazonSagemaker.js +3 -4
- package/dist/providers/googleVertex.d.ts +7 -0
- package/dist/providers/googleVertex.js +80 -2
- package/dist/rag/pipeline/RAGPipeline.d.ts +0 -5
- package/dist/rag/pipeline/RAGPipeline.js +122 -87
- package/dist/rag/ragIntegration.js +30 -0
- package/dist/rag/retrieval/hybridSearch.js +22 -0
- package/dist/server/abstract/baseServerAdapter.js +51 -19
- package/dist/server/middleware/common.js +44 -12
- package/dist/services/server/ai/observability/instrumentation.d.ts +2 -2
- package/dist/services/server/ai/observability/instrumentation.js +10 -5
- package/dist/types/conversationMemoryInterface.d.ts +2 -0
- package/dist/types/providers.d.ts +5 -0
- package/dist/utils/pricing.js +25 -1
- package/dist/utils/ttsProcessor.js +74 -59
- package/dist/workflow/config.d.ts +52 -52
- package/dist/workflow/core/ensembleExecutor.js +10 -0
- package/dist/workflow/core/judgeScorer.js +20 -2
- package/dist/workflow/core/workflowRunner.js +34 -1
- package/package.json +1 -1
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry Bridge
|
|
3
|
+
* Bidirectional context propagation between NeuroLink and OpenTelemetry
|
|
4
|
+
*/
|
|
5
|
+
import { context, propagation, trace, } from "@opentelemetry/api";
|
|
6
|
+
import { SpanStatus } from "./types/spanTypes.js";
|
|
7
|
+
import { SpanSerializer } from "./utils/spanSerializer.js";
|
|
8
|
+
/**
|
|
9
|
+
* Bridge for bidirectional context propagation between
|
|
10
|
+
* NeuroLink's observability system and OpenTelemetry
|
|
11
|
+
*/
|
|
12
|
+
export class OtelBridge {
|
|
13
|
+
tracer = trace.getTracer("neurolink-bridge");
|
|
14
|
+
/**
|
|
15
|
+
* Extract trace context from incoming request headers
|
|
16
|
+
*/
|
|
17
|
+
extractContext(headers) {
|
|
18
|
+
const extractedContext = propagation.extract(context.active(), headers);
|
|
19
|
+
const spanContext = trace.getSpanContext(extractedContext);
|
|
20
|
+
return spanContext ?? null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Inject trace context into outgoing request headers
|
|
24
|
+
*/
|
|
25
|
+
injectContext(headers) {
|
|
26
|
+
propagation.inject(context.active(), headers);
|
|
27
|
+
return headers;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create a NeuroLink span from OpenTelemetry context
|
|
31
|
+
*/
|
|
32
|
+
createSpanFromOtelContext(spanContext, type, name) {
|
|
33
|
+
return SpanSerializer.createSpan(type, name, {}, undefined, spanContext.traceId);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Wrap a function with OpenTelemetry tracing that also creates NeuroLink spans
|
|
37
|
+
*/
|
|
38
|
+
async wrapWithTracing(name, type, fn, onSpanEnd) {
|
|
39
|
+
const otelSpan = this.tracer.startSpan(name);
|
|
40
|
+
const neuroLinkSpan = SpanSerializer.createSpan(type, name, {}, undefined, otelSpan.spanContext().traceId);
|
|
41
|
+
try {
|
|
42
|
+
const result = await context.with(trace.setSpan(context.active(), otelSpan), () => fn(neuroLinkSpan));
|
|
43
|
+
const endedSpan = SpanSerializer.endSpan(neuroLinkSpan, SpanStatus.OK);
|
|
44
|
+
otelSpan.setStatus({ code: 1 }); // OK
|
|
45
|
+
if (onSpanEnd) {
|
|
46
|
+
onSpanEnd(endedSpan);
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
const endedSpan = SpanSerializer.endSpan(neuroLinkSpan, SpanStatus.ERROR, error instanceof Error ? error.message : String(error));
|
|
52
|
+
otelSpan.setStatus({
|
|
53
|
+
code: 2, // ERROR
|
|
54
|
+
message: error instanceof Error ? error.message : String(error),
|
|
55
|
+
});
|
|
56
|
+
otelSpan.recordException(error);
|
|
57
|
+
if (onSpanEnd) {
|
|
58
|
+
onSpanEnd(endedSpan);
|
|
59
|
+
}
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
otelSpan.end();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Convert NeuroLink span to OpenTelemetry span and export
|
|
68
|
+
*/
|
|
69
|
+
exportToOtel(span) {
|
|
70
|
+
const otelSpan = this.tracer.startSpan(span.name, {
|
|
71
|
+
startTime: new Date(span.startTime),
|
|
72
|
+
attributes: this.filterAttributes(span.attributes),
|
|
73
|
+
});
|
|
74
|
+
// Add events
|
|
75
|
+
for (const event of span.events) {
|
|
76
|
+
otelSpan.addEvent(event.name, this.filterEventAttributes(event.attributes), new Date(event.timestamp));
|
|
77
|
+
}
|
|
78
|
+
// Set status (map NeuroLink status to OTel status)
|
|
79
|
+
const otelStatusCode = span.status === SpanStatus.ERROR ? 2 : 1;
|
|
80
|
+
otelSpan.setStatus({
|
|
81
|
+
code: otelStatusCode,
|
|
82
|
+
message: span.statusMessage,
|
|
83
|
+
});
|
|
84
|
+
// End span
|
|
85
|
+
if (span.endTime) {
|
|
86
|
+
otelSpan.end(new Date(span.endTime));
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
otelSpan.end();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Get current trace context for correlation
|
|
94
|
+
*/
|
|
95
|
+
getCurrentTraceContext() {
|
|
96
|
+
const spanContext = trace.getActiveSpan()?.spanContext();
|
|
97
|
+
if (!spanContext) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
traceId: spanContext.traceId,
|
|
102
|
+
spanId: spanContext.spanId,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Filter attributes to only include OTel-compatible types
|
|
107
|
+
*/
|
|
108
|
+
filterAttributes(attrs) {
|
|
109
|
+
const result = {};
|
|
110
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
111
|
+
if (typeof value === "string" ||
|
|
112
|
+
typeof value === "number" ||
|
|
113
|
+
typeof value === "boolean") {
|
|
114
|
+
result[key] = value;
|
|
115
|
+
}
|
|
116
|
+
else if (value !== undefined && value !== null) {
|
|
117
|
+
result[key] = JSON.stringify(value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Filter event attributes
|
|
124
|
+
*/
|
|
125
|
+
filterEventAttributes(attrs) {
|
|
126
|
+
if (!attrs) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
return this.filterAttributes(attrs);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry Policy
|
|
3
|
+
* Configurable retry strategies for observability exporters
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Result of a retry decision
|
|
7
|
+
*/
|
|
8
|
+
export type RetryDecision = {
|
|
9
|
+
/** Whether to retry */
|
|
10
|
+
shouldRetry: boolean;
|
|
11
|
+
/** Delay before retry in milliseconds */
|
|
12
|
+
delayMs: number;
|
|
13
|
+
/** Reason for the decision */
|
|
14
|
+
reason: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Context for retry decision making
|
|
18
|
+
*/
|
|
19
|
+
export type RetryContext = {
|
|
20
|
+
/** Current attempt number (0-indexed) */
|
|
21
|
+
attempt: number;
|
|
22
|
+
/** The error that triggered the retry */
|
|
23
|
+
error: Error;
|
|
24
|
+
/** Total elapsed time since first attempt */
|
|
25
|
+
elapsedMs: number;
|
|
26
|
+
/** Operation name for logging */
|
|
27
|
+
operationName: string;
|
|
28
|
+
/** Additional metadata */
|
|
29
|
+
metadata?: Record<string, unknown>;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Retry policy interface
|
|
33
|
+
*/
|
|
34
|
+
export interface RetryPolicy {
|
|
35
|
+
/** Policy name for identification */
|
|
36
|
+
readonly name: string;
|
|
37
|
+
/** Decide whether to retry */
|
|
38
|
+
shouldRetry(context: RetryContext): RetryDecision;
|
|
39
|
+
/** Maximum attempts allowed */
|
|
40
|
+
readonly maxAttempts: number;
|
|
41
|
+
/** Maximum total time allowed for retries */
|
|
42
|
+
readonly maxTotalTimeMs: number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Base retry policy with common configuration
|
|
46
|
+
*/
|
|
47
|
+
export declare abstract class BaseRetryPolicy implements RetryPolicy {
|
|
48
|
+
abstract readonly name: string;
|
|
49
|
+
readonly maxAttempts: number;
|
|
50
|
+
readonly maxTotalTimeMs: number;
|
|
51
|
+
protected readonly retryableErrors: Set<string>;
|
|
52
|
+
protected readonly nonRetryableErrors: Set<string>;
|
|
53
|
+
constructor(config: {
|
|
54
|
+
maxAttempts?: number;
|
|
55
|
+
maxTotalTimeMs?: number;
|
|
56
|
+
retryableErrors?: string[];
|
|
57
|
+
nonRetryableErrors?: string[];
|
|
58
|
+
});
|
|
59
|
+
abstract shouldRetry(context: RetryContext): RetryDecision;
|
|
60
|
+
protected isRetryableError(error: Error): boolean;
|
|
61
|
+
protected extractErrorCode(error: Error): string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Exponential backoff retry policy
|
|
65
|
+
* Delay increases exponentially with each attempt
|
|
66
|
+
*/
|
|
67
|
+
export declare class ExponentialBackoffPolicy extends BaseRetryPolicy {
|
|
68
|
+
readonly name = "exponential-backoff";
|
|
69
|
+
private readonly baseDelayMs;
|
|
70
|
+
private readonly maxDelayMs;
|
|
71
|
+
private readonly jitterFactor;
|
|
72
|
+
constructor(config?: {
|
|
73
|
+
maxAttempts?: number;
|
|
74
|
+
maxTotalTimeMs?: number;
|
|
75
|
+
baseDelayMs?: number;
|
|
76
|
+
maxDelayMs?: number;
|
|
77
|
+
jitterFactor?: number;
|
|
78
|
+
retryableErrors?: string[];
|
|
79
|
+
nonRetryableErrors?: string[];
|
|
80
|
+
});
|
|
81
|
+
shouldRetry(context: RetryContext): RetryDecision;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Linear backoff retry policy
|
|
85
|
+
* Delay increases linearly with each attempt
|
|
86
|
+
*/
|
|
87
|
+
export declare class LinearBackoffPolicy extends BaseRetryPolicy {
|
|
88
|
+
readonly name = "linear-backoff";
|
|
89
|
+
private readonly delayIncrementMs;
|
|
90
|
+
private readonly initialDelayMs;
|
|
91
|
+
constructor(config?: {
|
|
92
|
+
maxAttempts?: number;
|
|
93
|
+
maxTotalTimeMs?: number;
|
|
94
|
+
initialDelayMs?: number;
|
|
95
|
+
delayIncrementMs?: number;
|
|
96
|
+
retryableErrors?: string[];
|
|
97
|
+
nonRetryableErrors?: string[];
|
|
98
|
+
});
|
|
99
|
+
shouldRetry(context: RetryContext): RetryDecision;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Fixed delay retry policy
|
|
103
|
+
* Same delay for all retry attempts
|
|
104
|
+
*/
|
|
105
|
+
export declare class FixedDelayPolicy extends BaseRetryPolicy {
|
|
106
|
+
readonly name = "fixed-delay";
|
|
107
|
+
private readonly delayMs;
|
|
108
|
+
constructor(config?: {
|
|
109
|
+
maxAttempts?: number;
|
|
110
|
+
maxTotalTimeMs?: number;
|
|
111
|
+
delayMs?: number;
|
|
112
|
+
retryableErrors?: string[];
|
|
113
|
+
nonRetryableErrors?: string[];
|
|
114
|
+
});
|
|
115
|
+
shouldRetry(context: RetryContext): RetryDecision;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* No retry policy - never retries
|
|
119
|
+
*/
|
|
120
|
+
export declare class NoRetryPolicy implements RetryPolicy {
|
|
121
|
+
readonly name = "no-retry";
|
|
122
|
+
readonly maxAttempts = 1;
|
|
123
|
+
readonly maxTotalTimeMs = 0;
|
|
124
|
+
shouldRetry(_context: RetryContext): RetryDecision;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Circuit breaker aware retry policy
|
|
128
|
+
* Works with circuit breaker state to prevent retries when circuit is open
|
|
129
|
+
*/
|
|
130
|
+
export declare class CircuitBreakerAwarePolicy extends BaseRetryPolicy {
|
|
131
|
+
readonly name = "circuit-breaker-aware";
|
|
132
|
+
private readonly innerPolicy;
|
|
133
|
+
private failures;
|
|
134
|
+
private lastFailure;
|
|
135
|
+
private circuitState;
|
|
136
|
+
private readonly failureThreshold;
|
|
137
|
+
private readonly resetTimeoutMs;
|
|
138
|
+
constructor(config?: {
|
|
139
|
+
innerPolicy?: RetryPolicy;
|
|
140
|
+
failureThreshold?: number;
|
|
141
|
+
resetTimeoutMs?: number;
|
|
142
|
+
maxAttempts?: number;
|
|
143
|
+
maxTotalTimeMs?: number;
|
|
144
|
+
retryableErrors?: string[];
|
|
145
|
+
nonRetryableErrors?: string[];
|
|
146
|
+
});
|
|
147
|
+
shouldRetry(context: RetryContext): RetryDecision;
|
|
148
|
+
recordFailure(): void;
|
|
149
|
+
recordSuccess(): void;
|
|
150
|
+
getCircuitState(): "closed" | "open" | "half-open";
|
|
151
|
+
reset(): void;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Retry executor - executes operations with retry policy
|
|
155
|
+
*/
|
|
156
|
+
export declare class RetryExecutor {
|
|
157
|
+
private readonly policy;
|
|
158
|
+
constructor(policy: RetryPolicy);
|
|
159
|
+
/**
|
|
160
|
+
* Execute an operation with retry
|
|
161
|
+
* @param operation - The async operation to execute
|
|
162
|
+
* @param operationName - Name for logging
|
|
163
|
+
* @returns The result of the operation
|
|
164
|
+
* @throws The last error if all retries fail
|
|
165
|
+
*/
|
|
166
|
+
execute<T>(operation: () => Promise<T>, operationName: string): Promise<T>;
|
|
167
|
+
private delay;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Factory for creating retry policies
|
|
171
|
+
*/
|
|
172
|
+
export declare class RetryPolicyFactory {
|
|
173
|
+
/**
|
|
174
|
+
* Create a default policy for production use
|
|
175
|
+
*/
|
|
176
|
+
static createDefault(): ExponentialBackoffPolicy;
|
|
177
|
+
/**
|
|
178
|
+
* Create an aggressive policy for critical operations
|
|
179
|
+
*/
|
|
180
|
+
static createAggressive(): ExponentialBackoffPolicy;
|
|
181
|
+
/**
|
|
182
|
+
* Create a conservative policy for non-critical operations
|
|
183
|
+
*/
|
|
184
|
+
static createConservative(): LinearBackoffPolicy;
|
|
185
|
+
/**
|
|
186
|
+
* Create a circuit breaker aware policy
|
|
187
|
+
*/
|
|
188
|
+
static createWithCircuitBreaker(config?: {
|
|
189
|
+
failureThreshold?: number;
|
|
190
|
+
resetTimeoutMs?: number;
|
|
191
|
+
}): CircuitBreakerAwarePolicy;
|
|
192
|
+
}
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry Policy
|
|
3
|
+
* Configurable retry strategies for observability exporters
|
|
4
|
+
*/
|
|
5
|
+
import { logger } from "../utils/logger.js";
|
|
6
|
+
/**
|
|
7
|
+
* Base retry policy with common configuration
|
|
8
|
+
*/
|
|
9
|
+
export class BaseRetryPolicy {
|
|
10
|
+
maxAttempts;
|
|
11
|
+
maxTotalTimeMs;
|
|
12
|
+
retryableErrors;
|
|
13
|
+
nonRetryableErrors;
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.maxAttempts = config.maxAttempts ?? 3;
|
|
16
|
+
this.maxTotalTimeMs = config.maxTotalTimeMs ?? 60000; // 1 minute
|
|
17
|
+
this.retryableErrors = new Set(config.retryableErrors ?? [
|
|
18
|
+
"ECONNRESET",
|
|
19
|
+
"ETIMEDOUT",
|
|
20
|
+
"ECONNREFUSED",
|
|
21
|
+
"EPIPE",
|
|
22
|
+
"ENOTFOUND",
|
|
23
|
+
"ENETUNREACH",
|
|
24
|
+
"EAI_AGAIN",
|
|
25
|
+
"429", // Rate limit
|
|
26
|
+
"500", // Internal server error
|
|
27
|
+
"502", // Bad gateway
|
|
28
|
+
"503", // Service unavailable
|
|
29
|
+
"504", // Gateway timeout
|
|
30
|
+
]);
|
|
31
|
+
this.nonRetryableErrors = new Set(config.nonRetryableErrors ?? [
|
|
32
|
+
"400", // Bad request
|
|
33
|
+
"401", // Unauthorized
|
|
34
|
+
"403", // Forbidden
|
|
35
|
+
"404", // Not found
|
|
36
|
+
"422", // Unprocessable entity
|
|
37
|
+
]);
|
|
38
|
+
}
|
|
39
|
+
isRetryableError(error) {
|
|
40
|
+
const errorCode = this.extractErrorCode(error);
|
|
41
|
+
// Check if explicitly non-retryable
|
|
42
|
+
if (this.nonRetryableErrors.has(errorCode)) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
// Check if explicitly retryable
|
|
46
|
+
if (this.retryableErrors.has(errorCode)) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
// Default: retry on network-like errors
|
|
50
|
+
return (error.message.includes("timeout") ||
|
|
51
|
+
error.message.includes("ECONNRESET") ||
|
|
52
|
+
error.message.includes("network"));
|
|
53
|
+
}
|
|
54
|
+
extractErrorCode(error) {
|
|
55
|
+
// Check for HTTP status code in error
|
|
56
|
+
const httpMatch = error.message.match(/(\d{3})/);
|
|
57
|
+
if (httpMatch) {
|
|
58
|
+
return httpMatch[1];
|
|
59
|
+
}
|
|
60
|
+
// Check for Node.js error code
|
|
61
|
+
const nodeError = error;
|
|
62
|
+
if (nodeError.code) {
|
|
63
|
+
return nodeError.code;
|
|
64
|
+
}
|
|
65
|
+
return "UNKNOWN";
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Exponential backoff retry policy
|
|
70
|
+
* Delay increases exponentially with each attempt
|
|
71
|
+
*/
|
|
72
|
+
export class ExponentialBackoffPolicy extends BaseRetryPolicy {
|
|
73
|
+
name = "exponential-backoff";
|
|
74
|
+
baseDelayMs;
|
|
75
|
+
maxDelayMs;
|
|
76
|
+
jitterFactor;
|
|
77
|
+
constructor(config) {
|
|
78
|
+
super(config ?? {});
|
|
79
|
+
this.baseDelayMs = config?.baseDelayMs ?? 1000;
|
|
80
|
+
this.maxDelayMs = config?.maxDelayMs ?? 30000;
|
|
81
|
+
this.jitterFactor = config?.jitterFactor ?? 0.1;
|
|
82
|
+
}
|
|
83
|
+
shouldRetry(context) {
|
|
84
|
+
// Check max attempts
|
|
85
|
+
if (context.attempt >= this.maxAttempts) {
|
|
86
|
+
return {
|
|
87
|
+
shouldRetry: false,
|
|
88
|
+
delayMs: 0,
|
|
89
|
+
reason: `Max attempts (${this.maxAttempts}) exceeded`,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// Check max total time
|
|
93
|
+
if (context.elapsedMs >= this.maxTotalTimeMs) {
|
|
94
|
+
return {
|
|
95
|
+
shouldRetry: false,
|
|
96
|
+
delayMs: 0,
|
|
97
|
+
reason: `Max total time (${this.maxTotalTimeMs}ms) exceeded`,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Check if error is retryable
|
|
101
|
+
if (!this.isRetryableError(context.error)) {
|
|
102
|
+
return {
|
|
103
|
+
shouldRetry: false,
|
|
104
|
+
delayMs: 0,
|
|
105
|
+
reason: `Error not retryable: ${context.error.message}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// Calculate delay with exponential backoff
|
|
109
|
+
const exponentialDelay = this.baseDelayMs * 2 ** context.attempt;
|
|
110
|
+
const cappedDelay = Math.min(exponentialDelay, this.maxDelayMs);
|
|
111
|
+
// Add jitter
|
|
112
|
+
const jitter = cappedDelay * this.jitterFactor * (Math.random() * 2 - 1);
|
|
113
|
+
const delayMs = Math.max(0, Math.round(cappedDelay + jitter));
|
|
114
|
+
return {
|
|
115
|
+
shouldRetry: true,
|
|
116
|
+
delayMs,
|
|
117
|
+
reason: `Retrying after ${delayMs}ms (attempt ${context.attempt + 1}/${this.maxAttempts})`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Linear backoff retry policy
|
|
123
|
+
* Delay increases linearly with each attempt
|
|
124
|
+
*/
|
|
125
|
+
export class LinearBackoffPolicy extends BaseRetryPolicy {
|
|
126
|
+
name = "linear-backoff";
|
|
127
|
+
delayIncrementMs;
|
|
128
|
+
initialDelayMs;
|
|
129
|
+
constructor(config) {
|
|
130
|
+
super(config ?? {});
|
|
131
|
+
this.initialDelayMs = config?.initialDelayMs ?? 1000;
|
|
132
|
+
this.delayIncrementMs = config?.delayIncrementMs ?? 1000;
|
|
133
|
+
}
|
|
134
|
+
shouldRetry(context) {
|
|
135
|
+
if (context.attempt >= this.maxAttempts) {
|
|
136
|
+
return {
|
|
137
|
+
shouldRetry: false,
|
|
138
|
+
delayMs: 0,
|
|
139
|
+
reason: `Max attempts (${this.maxAttempts}) exceeded`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (context.elapsedMs >= this.maxTotalTimeMs) {
|
|
143
|
+
return {
|
|
144
|
+
shouldRetry: false,
|
|
145
|
+
delayMs: 0,
|
|
146
|
+
reason: `Max total time (${this.maxTotalTimeMs}ms) exceeded`,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
if (!this.isRetryableError(context.error)) {
|
|
150
|
+
return {
|
|
151
|
+
shouldRetry: false,
|
|
152
|
+
delayMs: 0,
|
|
153
|
+
reason: `Error not retryable: ${context.error.message}`,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const delayMs = this.initialDelayMs + context.attempt * this.delayIncrementMs;
|
|
157
|
+
return {
|
|
158
|
+
shouldRetry: true,
|
|
159
|
+
delayMs,
|
|
160
|
+
reason: `Retrying after ${delayMs}ms (attempt ${context.attempt + 1}/${this.maxAttempts})`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Fixed delay retry policy
|
|
166
|
+
* Same delay for all retry attempts
|
|
167
|
+
*/
|
|
168
|
+
export class FixedDelayPolicy extends BaseRetryPolicy {
|
|
169
|
+
name = "fixed-delay";
|
|
170
|
+
delayMs;
|
|
171
|
+
constructor(config) {
|
|
172
|
+
super(config ?? {});
|
|
173
|
+
this.delayMs = config?.delayMs ?? 1000;
|
|
174
|
+
}
|
|
175
|
+
shouldRetry(context) {
|
|
176
|
+
if (context.attempt >= this.maxAttempts) {
|
|
177
|
+
return {
|
|
178
|
+
shouldRetry: false,
|
|
179
|
+
delayMs: 0,
|
|
180
|
+
reason: `Max attempts (${this.maxAttempts}) exceeded`,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
if (context.elapsedMs >= this.maxTotalTimeMs) {
|
|
184
|
+
return {
|
|
185
|
+
shouldRetry: false,
|
|
186
|
+
delayMs: 0,
|
|
187
|
+
reason: `Max total time (${this.maxTotalTimeMs}ms) exceeded`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (!this.isRetryableError(context.error)) {
|
|
191
|
+
return {
|
|
192
|
+
shouldRetry: false,
|
|
193
|
+
delayMs: 0,
|
|
194
|
+
reason: `Error not retryable: ${context.error.message}`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
shouldRetry: true,
|
|
199
|
+
delayMs: this.delayMs,
|
|
200
|
+
reason: `Retrying after ${this.delayMs}ms (attempt ${context.attempt + 1}/${this.maxAttempts})`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* No retry policy - never retries
|
|
206
|
+
*/
|
|
207
|
+
export class NoRetryPolicy {
|
|
208
|
+
name = "no-retry";
|
|
209
|
+
maxAttempts = 1;
|
|
210
|
+
maxTotalTimeMs = 0;
|
|
211
|
+
shouldRetry(_context) {
|
|
212
|
+
return {
|
|
213
|
+
shouldRetry: false,
|
|
214
|
+
delayMs: 0,
|
|
215
|
+
reason: "Retry disabled",
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Circuit breaker aware retry policy
|
|
221
|
+
* Works with circuit breaker state to prevent retries when circuit is open
|
|
222
|
+
*/
|
|
223
|
+
export class CircuitBreakerAwarePolicy extends BaseRetryPolicy {
|
|
224
|
+
name = "circuit-breaker-aware";
|
|
225
|
+
innerPolicy;
|
|
226
|
+
failures = 0;
|
|
227
|
+
lastFailure = 0;
|
|
228
|
+
circuitState = "closed";
|
|
229
|
+
failureThreshold;
|
|
230
|
+
resetTimeoutMs;
|
|
231
|
+
constructor(config) {
|
|
232
|
+
super(config ?? {});
|
|
233
|
+
this.innerPolicy = config?.innerPolicy ?? new ExponentialBackoffPolicy();
|
|
234
|
+
this.failureThreshold = config?.failureThreshold ?? 5;
|
|
235
|
+
this.resetTimeoutMs = config?.resetTimeoutMs ?? 30000;
|
|
236
|
+
}
|
|
237
|
+
shouldRetry(context) {
|
|
238
|
+
// Check circuit state
|
|
239
|
+
if (this.circuitState === "open") {
|
|
240
|
+
if (Date.now() - this.lastFailure > this.resetTimeoutMs) {
|
|
241
|
+
this.circuitState = "half-open";
|
|
242
|
+
logger.info(`[${this.name}] Circuit half-open, allowing probe request`);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
return {
|
|
246
|
+
shouldRetry: false,
|
|
247
|
+
delayMs: 0,
|
|
248
|
+
reason: "Circuit breaker open",
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Delegate to inner policy
|
|
253
|
+
const decision = this.innerPolicy.shouldRetry(context);
|
|
254
|
+
// Record result for circuit breaker
|
|
255
|
+
if (!decision.shouldRetry) {
|
|
256
|
+
this.recordFailure();
|
|
257
|
+
}
|
|
258
|
+
return decision;
|
|
259
|
+
}
|
|
260
|
+
recordFailure() {
|
|
261
|
+
this.failures++;
|
|
262
|
+
this.lastFailure = Date.now();
|
|
263
|
+
if (this.failures >= this.failureThreshold) {
|
|
264
|
+
this.circuitState = "open";
|
|
265
|
+
logger.warn(`[${this.name}] Circuit opened after ${this.failures} failures`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
recordSuccess() {
|
|
269
|
+
if (this.circuitState === "half-open") {
|
|
270
|
+
logger.info(`[${this.name}] Circuit closed after successful request`);
|
|
271
|
+
}
|
|
272
|
+
this.failures = 0;
|
|
273
|
+
this.circuitState = "closed";
|
|
274
|
+
}
|
|
275
|
+
getCircuitState() {
|
|
276
|
+
return this.circuitState;
|
|
277
|
+
}
|
|
278
|
+
reset() {
|
|
279
|
+
this.failures = 0;
|
|
280
|
+
this.circuitState = "closed";
|
|
281
|
+
this.lastFailure = 0;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Retry executor - executes operations with retry policy
|
|
286
|
+
*/
|
|
287
|
+
export class RetryExecutor {
|
|
288
|
+
policy;
|
|
289
|
+
constructor(policy) {
|
|
290
|
+
this.policy = policy;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Execute an operation with retry
|
|
294
|
+
* @param operation - The async operation to execute
|
|
295
|
+
* @param operationName - Name for logging
|
|
296
|
+
* @returns The result of the operation
|
|
297
|
+
* @throws The last error if all retries fail
|
|
298
|
+
*/
|
|
299
|
+
async execute(operation, operationName) {
|
|
300
|
+
const startTime = Date.now();
|
|
301
|
+
let lastError;
|
|
302
|
+
let attempt = 0;
|
|
303
|
+
while (attempt < this.policy.maxAttempts) {
|
|
304
|
+
try {
|
|
305
|
+
const result = await operation();
|
|
306
|
+
// Record success if policy supports it
|
|
307
|
+
if (this.policy instanceof CircuitBreakerAwarePolicy) {
|
|
308
|
+
this.policy.recordSuccess();
|
|
309
|
+
}
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
catch (error) {
|
|
313
|
+
lastError = error;
|
|
314
|
+
const elapsed = Date.now() - startTime;
|
|
315
|
+
const decision = this.policy.shouldRetry({
|
|
316
|
+
attempt,
|
|
317
|
+
error: lastError,
|
|
318
|
+
elapsedMs: elapsed,
|
|
319
|
+
operationName,
|
|
320
|
+
});
|
|
321
|
+
if (!decision.shouldRetry) {
|
|
322
|
+
logger.warn(`[RetryExecutor] ${operationName}: ${decision.reason}`);
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
logger.debug(`[RetryExecutor] ${operationName}: ${decision.reason}`);
|
|
326
|
+
await this.delay(decision.delayMs);
|
|
327
|
+
attempt++;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
throw (lastError ??
|
|
331
|
+
new Error(`${operationName} failed after ${attempt} attempts`));
|
|
332
|
+
}
|
|
333
|
+
delay(ms) {
|
|
334
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Factory for creating retry policies
|
|
339
|
+
*/
|
|
340
|
+
export class RetryPolicyFactory {
|
|
341
|
+
/**
|
|
342
|
+
* Create a default policy for production use
|
|
343
|
+
*/
|
|
344
|
+
static createDefault() {
|
|
345
|
+
return new ExponentialBackoffPolicy({
|
|
346
|
+
maxAttempts: 3,
|
|
347
|
+
baseDelayMs: 1000,
|
|
348
|
+
maxDelayMs: 30000,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Create an aggressive policy for critical operations
|
|
353
|
+
*/
|
|
354
|
+
static createAggressive() {
|
|
355
|
+
return new ExponentialBackoffPolicy({
|
|
356
|
+
maxAttempts: 5,
|
|
357
|
+
maxTotalTimeMs: 120000,
|
|
358
|
+
baseDelayMs: 500,
|
|
359
|
+
maxDelayMs: 60000,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Create a conservative policy for non-critical operations
|
|
364
|
+
*/
|
|
365
|
+
static createConservative() {
|
|
366
|
+
return new LinearBackoffPolicy({
|
|
367
|
+
maxAttempts: 2,
|
|
368
|
+
maxTotalTimeMs: 10000,
|
|
369
|
+
initialDelayMs: 500,
|
|
370
|
+
delayIncrementMs: 500,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Create a circuit breaker aware policy
|
|
375
|
+
*/
|
|
376
|
+
static createWithCircuitBreaker(config) {
|
|
377
|
+
return new CircuitBreakerAwarePolicy({
|
|
378
|
+
innerPolicy: RetryPolicyFactory.createDefault(),
|
|
379
|
+
failureThreshold: config?.failureThreshold ?? 5,
|
|
380
|
+
resetTimeoutMs: config?.resetTimeoutMs ?? 30000,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|