@emmvish/stable-request 2.2.0 → 2.2.1
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 +190 -0
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +1 -0
- package/dist/constants/index.js.map +1 -1
- package/dist/core/stable-function.d.ts.map +1 -1
- package/dist/core/stable-function.js +185 -169
- package/dist/core/stable-function.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utilities/execute-branch-workflow.d.ts.map +1 -1
- package/dist/utilities/execute-branch-workflow.js +5 -1
- package/dist/utilities/execute-branch-workflow.js.map +1 -1
- package/dist/utilities/execute-phase.d.ts.map +1 -1
- package/dist/utilities/execute-phase.js +3 -0
- package/dist/utilities/execute-phase.js.map +1 -1
- package/dist/utilities/execute-sequentially.d.ts.map +1 -1
- package/dist/utilities/execute-sequentially.js +0 -1
- package/dist/utilities/execute-sequentially.js.map +1 -1
- package/dist/utilities/execute-with-timeout.d.ts +6 -0
- package/dist/utilities/execute-with-timeout.d.ts.map +1 -0
- package/dist/utilities/execute-with-timeout.js +28 -0
- package/dist/utilities/execute-with-timeout.js.map +1 -0
- package/dist/utilities/index.d.ts +1 -0
- package/dist/utilities/index.d.ts.map +1 -1
- package/dist/utilities/index.js +1 -0
- package/dist/utilities/index.js.map +1 -1
- package/dist/utilities/metrics-aggregator.d.ts +0 -36
- package/dist/utilities/metrics-aggregator.d.ts.map +1 -1
- package/dist/utilities/metrics-aggregator.js +1 -42
- package/dist/utilities/metrics-aggregator.js.map +1 -1
- package/dist/utilities/prepare-api-function-options.d.ts.map +1 -1
- package/dist/utilities/prepare-api-function-options.js +1 -0
- package/dist/utilities/prepare-api-function-options.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -470,6 +470,196 @@ console.log(`Graph workflow success: ${result.success}`);
|
|
|
470
470
|
|
|
471
471
|
## Resilience Mechanisms
|
|
472
472
|
|
|
473
|
+
### Execution Timeouts
|
|
474
|
+
|
|
475
|
+
Set maximum execution time for functions to prevent indefinite hangs. Timeouts are enforced at multiple levels with proper inheritance.
|
|
476
|
+
|
|
477
|
+
#### Function-Level Timeout
|
|
478
|
+
|
|
479
|
+
Set timeout directly on a function:
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
import { stableFunction } from '@emmvish/stable-request';
|
|
483
|
+
|
|
484
|
+
const result = await stableFunction({
|
|
485
|
+
fn: async () => {
|
|
486
|
+
// Long-running operation
|
|
487
|
+
await processLargeDataset();
|
|
488
|
+
return 'success';
|
|
489
|
+
},
|
|
490
|
+
args: [],
|
|
491
|
+
returnResult: true,
|
|
492
|
+
executionTimeout: 5000, // 5 seconds max
|
|
493
|
+
attempts: 3,
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
if (!result.success && result.error?.includes('timeout')) {
|
|
497
|
+
console.log('Function timed out');
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
#### Gateway-Level Timeout
|
|
502
|
+
|
|
503
|
+
Apply timeout to all functions in a gateway:
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
import { stableApiGateway, RequestOrFunction } from '@emmvish/stable-request';
|
|
507
|
+
|
|
508
|
+
const results = await stableApiGateway(
|
|
509
|
+
[
|
|
510
|
+
{
|
|
511
|
+
type: RequestOrFunction.FUNCTION,
|
|
512
|
+
function: {
|
|
513
|
+
id: 'task1',
|
|
514
|
+
functionOptions: {
|
|
515
|
+
fn: async () => await task1(),
|
|
516
|
+
args: [],
|
|
517
|
+
// No timeout specified - inherits from gateway
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
type: RequestOrFunction.FUNCTION,
|
|
523
|
+
function: {
|
|
524
|
+
id: 'task2',
|
|
525
|
+
functionOptions: {
|
|
526
|
+
fn: async () => await task2(),
|
|
527
|
+
args: [],
|
|
528
|
+
executionTimeout: 10000, // Override gateway timeout
|
|
529
|
+
},
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
],
|
|
533
|
+
{
|
|
534
|
+
commonExecutionTimeout: 3000, // Default 3s for all functions
|
|
535
|
+
}
|
|
536
|
+
);
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
#### Request Group Timeout
|
|
540
|
+
|
|
541
|
+
Different timeouts for different groups:
|
|
542
|
+
|
|
543
|
+
```typescript
|
|
544
|
+
const results = await stableApiGateway(
|
|
545
|
+
[
|
|
546
|
+
{
|
|
547
|
+
type: RequestOrFunction.FUNCTION,
|
|
548
|
+
function: {
|
|
549
|
+
id: 'critical',
|
|
550
|
+
groupId: 'criticalOps',
|
|
551
|
+
functionOptions: { fn: criticalOp, args: [] },
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
type: RequestOrFunction.FUNCTION,
|
|
556
|
+
function: {
|
|
557
|
+
id: 'background',
|
|
558
|
+
groupId: 'backgroundOps',
|
|
559
|
+
functionOptions: { fn: backgroundOp, args: [] },
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
],
|
|
563
|
+
{
|
|
564
|
+
requestGroups: [
|
|
565
|
+
{
|
|
566
|
+
id: 'criticalOps',
|
|
567
|
+
commonConfig: {
|
|
568
|
+
commonExecutionTimeout: 1000, // Strict 1s timeout
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
{
|
|
572
|
+
id: 'backgroundOps',
|
|
573
|
+
commonConfig: {
|
|
574
|
+
commonExecutionTimeout: 30000, // Lenient 30s timeout
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
],
|
|
578
|
+
}
|
|
579
|
+
);
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
#### Workflow Phase Timeout
|
|
583
|
+
|
|
584
|
+
Apply timeout at phase level in workflows:
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
import { stableWorkflow } from '@emmvish/stable-request';
|
|
588
|
+
|
|
589
|
+
const result = await stableWorkflow(
|
|
590
|
+
[
|
|
591
|
+
{
|
|
592
|
+
id: 'initialization',
|
|
593
|
+
functions: [
|
|
594
|
+
{
|
|
595
|
+
id: 'init',
|
|
596
|
+
functionOptions: {
|
|
597
|
+
fn: initializeSystem,
|
|
598
|
+
args: [],
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
],
|
|
602
|
+
commonConfig: {
|
|
603
|
+
commonExecutionTimeout: 5000, // 5s for initialization
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
id: 'processing',
|
|
608
|
+
functions: [
|
|
609
|
+
{
|
|
610
|
+
id: 'process',
|
|
611
|
+
functionOptions: {
|
|
612
|
+
fn: processData,
|
|
613
|
+
args: [],
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
],
|
|
617
|
+
commonConfig: {
|
|
618
|
+
commonExecutionTimeout: 30000, // 30s for processing
|
|
619
|
+
},
|
|
620
|
+
},
|
|
621
|
+
],
|
|
622
|
+
{
|
|
623
|
+
commonExecutionTimeout: 10000, // Default for phases without specific timeout
|
|
624
|
+
}
|
|
625
|
+
);
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
#### Timeout Precedence
|
|
629
|
+
|
|
630
|
+
Timeouts follow the configuration cascade pattern:
|
|
631
|
+
|
|
632
|
+
**Function > Group > Phase/Branch > Gateway**
|
|
633
|
+
|
|
634
|
+
- Function-level `executionTimeout` always wins
|
|
635
|
+
- If not set, inherits from request group's `commonExecutionTimeout`
|
|
636
|
+
- If not set, inherits from phase/branch's `commonExecutionTimeout`
|
|
637
|
+
- If not set, inherits from gateway's `commonExecutionTimeout`
|
|
638
|
+
- If not set, no timeout is applied
|
|
639
|
+
|
|
640
|
+
#### Timeout Behavior
|
|
641
|
+
|
|
642
|
+
- Timeout applies to **entire function execution** including all retry attempts
|
|
643
|
+
- When timeout is exceeded, function returns failed result with timeout error
|
|
644
|
+
- Timeout does NOT stop execution mid-flight (no AbortController)
|
|
645
|
+
- Metrics are still collected even when timeout occurs
|
|
646
|
+
- Use with retries: timeout encompasses all attempts, not per-attempt
|
|
647
|
+
|
|
648
|
+
```typescript
|
|
649
|
+
const result = await stableFunction({
|
|
650
|
+
fn: slowFunction,
|
|
651
|
+
args: [],
|
|
652
|
+
attempts: 5,
|
|
653
|
+
wait: 1000,
|
|
654
|
+
executionTimeout: 3000, // Total time for all 5 attempts
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
// If each attempt takes 800ms:
|
|
658
|
+
// - Attempt 1: 800ms
|
|
659
|
+
// - Attempt 2: starts at 1800ms (after 1s wait)
|
|
660
|
+
// - Attempt 3: would start at 3600ms → TIMEOUT at 3000ms
|
|
661
|
+
```
|
|
662
|
+
|
|
473
663
|
### Retry Strategies
|
|
474
664
|
|
|
475
665
|
When a request or function fails and is retryable, retry with configurable backoff.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAElF,eAAO,MAAM,wBAAwB,EAAE,CAAC,MAAM,mBAAmB,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAElF,eAAO,MAAM,wBAAwB,EAAE,CAAC,MAAM,mBAAmB,CAAC,EAsBjE,CAAC;AAEF,eAAO,MAAM,+BAA+B,EAAE,wBAAwB,EAqBrE,CAAC;AAEF,eAAO,MAAM,gCAAgC,gHAMnC,CAAC;AAEX,eAAO,MAAM,oCAAoC,2JASvC,CAAC;AAEX,eAAO,MAAM,iCAAiC,qPAapC,CAAC;AAEX,eAAO,MAAM,wCAAwC,6DAI3C,CAAC;AAEX,eAAO,MAAM,8BAA8B,2EAKjC,CAAC;AAEX,eAAO,MAAM,qCAAqC,2FAKxC,CAAC;AAEX,eAAO,MAAM,4CAA4C,2EAI/C,CAAC;AAEX,eAAO,MAAM,8BAA8B,iIAOjC,CAAC;AAEX,eAAO,MAAM,+BAA+B,oLAUlC,CAAC"}
|
package/dist/constants/index.js
CHANGED
|
@@ -19,6 +19,7 @@ export const extractCommonOptionsKeys = [
|
|
|
19
19
|
'commonTrialMode',
|
|
20
20
|
'commonCache',
|
|
21
21
|
'commonStatePersistence',
|
|
22
|
+
'commonExecutionTimeout',
|
|
22
23
|
];
|
|
23
24
|
export const PrepareApiRequestOptionsMapping = [
|
|
24
25
|
{ localKey: 'preExecution', commonKey: 'commonPreExecution', groupCommonKey: 'commonPreExecution', targetKey: 'preExecution' },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stable-function.d.ts","sourceRoot":"","sources":["../../src/core/stable-function.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,eAAe,EACf,sBAAsB,EAEvB,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"stable-function.d.ts","sourceRoot":"","sources":["../../src/core/stable-function.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,eAAe,EACf,sBAAsB,EAEvB,MAAM,mBAAmB,CAAC;AAqB3B,wBAAsB,cAAc,CAAC,KAAK,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,OAAO,GAAG,GAAG,EAC7E,OAAO,EAAE,eAAe,CAAC,KAAK,EAAE,OAAO,CAAC,GACvC,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAkc1C"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RETRY_STRATEGIES, CircuitBreakerState } from '../enums/index.js';
|
|
2
|
-
import { CircuitBreaker, CircuitBreakerOpenError, executeWithPersistence, formatLogContext, getGlobalFunctionCacheManager, getNewDelayTime, delay, fnExec, safelyStringify, validateTrialModeProbabilities, MetricsAggregator, MetricsValidator, RateLimiter, ConcurrencyLimiter } from '../utilities/index.js';
|
|
2
|
+
import { CircuitBreaker, CircuitBreakerOpenError, executeWithPersistence, executeWithTimeout, TimeoutError, formatLogContext, getGlobalFunctionCacheManager, getNewDelayTime, delay, fnExec, safelyStringify, validateTrialModeProbabilities, MetricsAggregator, MetricsValidator, RateLimiter, ConcurrencyLimiter } from '../utilities/index.js';
|
|
3
3
|
export async function stableFunction(options) {
|
|
4
4
|
const { preExecution = {
|
|
5
5
|
preExecutionHook: ({ inputParams, commonBuffer }) => { },
|
|
@@ -27,7 +27,7 @@ export async function stableFunction(options) {
|
|
|
27
27
|
throw e;
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
-
const { fn, args, responseAnalyzer = ({ data, trialMode = { enabled: false } }) => true, returnResult = false, attempts: givenAttempts = 1, performAllAttempts = false, wait = 1000, maxAllowedWait = 60000, retryStrategy = RETRY_STRATEGIES.FIXED, logAllErrors = false, handleErrors = ({ fn, args, errorLog, maxSerializableChars = 1000, executionContext }) => console.error(`${formatLogContext(executionContext)}stable-request:\n`, `Function: ${fn.name || 'anonymous'}\n`, 'Args:\n', safelyStringify(args, maxSerializableChars), '\nError log:\n', safelyStringify(errorLog, maxSerializableChars)), logAllSuccessfulAttempts = false, handleSuccessfulAttemptData = ({ fn, args, successfulAttemptData, maxSerializableChars = 1000, executionContext }) => console.info(`${formatLogContext(executionContext)}stable-request:\n`, `Function: ${fn.name || 'anonymous'}\n`, 'Args:\n', safelyStringify(args, maxSerializableChars), '\nSuccessful attempt:\n', safelyStringify(successfulAttemptData, maxSerializableChars)), maxSerializableChars = 1000, finalErrorAnalyzer = ({ fn, args, error, trialMode = { enabled: false } }) => false, trialMode = { enabled: false }, hookParams = {}, cache, circuitBreaker, jitter = 0, statePersistence, rateLimit, maxConcurrentRequests } = options;
|
|
30
|
+
const { fn, args, responseAnalyzer = ({ data, trialMode = { enabled: false } }) => true, returnResult = false, attempts: givenAttempts = 1, performAllAttempts = false, wait = 1000, maxAllowedWait = 60000, retryStrategy = RETRY_STRATEGIES.FIXED, logAllErrors = false, handleErrors = ({ fn, args, errorLog, maxSerializableChars = 1000, executionContext }) => console.error(`${formatLogContext(executionContext)}stable-request:\n`, `Function: ${fn.name || 'anonymous'}\n`, 'Args:\n', safelyStringify(args, maxSerializableChars), '\nError log:\n', safelyStringify(errorLog, maxSerializableChars)), logAllSuccessfulAttempts = false, handleSuccessfulAttemptData = ({ fn, args, successfulAttemptData, maxSerializableChars = 1000, executionContext }) => console.info(`${formatLogContext(executionContext)}stable-request:\n`, `Function: ${fn.name || 'anonymous'}\n`, 'Args:\n', safelyStringify(args, maxSerializableChars), '\nSuccessful attempt:\n', safelyStringify(successfulAttemptData, maxSerializableChars)), maxSerializableChars = 1000, finalErrorAnalyzer = ({ fn, args, error, trialMode = { enabled: false } }) => false, trialMode = { enabled: false }, hookParams = {}, cache, circuitBreaker, jitter = 0, statePersistence, rateLimit, maxConcurrentRequests, executionTimeout } = options;
|
|
31
31
|
let attempts = givenAttempts;
|
|
32
32
|
const functionStartTime = Date.now();
|
|
33
33
|
const errorLogs = [];
|
|
@@ -76,210 +76,226 @@ export async function stableFunction(options) {
|
|
|
76
76
|
if (maxConcurrentRequests && maxConcurrentRequests > 0) {
|
|
77
77
|
concurrencyLimiterInstance = new ConcurrencyLimiter(maxConcurrentRequests);
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
79
|
+
const executeFunction = async () => {
|
|
80
|
+
try {
|
|
81
|
+
validateTrialModeProbabilities(trialMode);
|
|
82
|
+
let res = {
|
|
83
|
+
ok: false,
|
|
84
|
+
isRetryable: true,
|
|
85
|
+
timestamp: new Date().toISOString(),
|
|
86
|
+
executionTime: 0
|
|
87
|
+
};
|
|
88
|
+
const maxAttempts = attempts;
|
|
89
|
+
let lastSuccessfulAttemptData = undefined;
|
|
90
|
+
let hadAtLeastOneSuccess = false;
|
|
91
|
+
do {
|
|
92
|
+
attempts--;
|
|
93
|
+
const currentAttempt = maxAttempts - attempts;
|
|
94
|
+
totalAttemptsMade = currentAttempt;
|
|
95
|
+
if (circuitBreakerInstance) {
|
|
96
|
+
const cbConfig = circuitBreakerInstance.getState().config;
|
|
97
|
+
if (cbConfig.trackIndividualAttempts || currentAttempt === 1) {
|
|
98
|
+
const canExecute = await circuitBreakerInstance.canExecute();
|
|
99
|
+
if (!canExecute) {
|
|
100
|
+
throw new CircuitBreakerOpenError(`${formatLogContext(executionContext)}stable-request: Circuit breaker is ${circuitBreakerInstance.getState().state}. Function execution blocked at attempt ${currentAttempt}.`);
|
|
101
|
+
}
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
104
|
+
try {
|
|
105
|
+
const executeAttempt = async () => {
|
|
106
|
+
return await fnExec(fn, args, returnResult, maxSerializableChars, trialMode, cache, executionContext);
|
|
107
|
+
};
|
|
108
|
+
if (rateLimiterInstance && concurrencyLimiterInstance) {
|
|
109
|
+
res = await rateLimiterInstance.execute(() => concurrencyLimiterInstance.execute(executeAttempt));
|
|
110
|
+
}
|
|
111
|
+
else if (rateLimiterInstance) {
|
|
112
|
+
res = await rateLimiterInstance.execute(executeAttempt);
|
|
113
|
+
}
|
|
114
|
+
else if (concurrencyLimiterInstance) {
|
|
115
|
+
res = await concurrencyLimiterInstance.execute(executeAttempt);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
res = await executeAttempt();
|
|
119
|
+
}
|
|
120
|
+
if (res.fromCache && res.ok) {
|
|
121
|
+
if (trialMode.enabled) {
|
|
122
|
+
console.info(`${formatLogContext(executionContext)}stable-request: Response served from cache:\n`, safelyStringify(res?.data, maxSerializableChars));
|
|
123
|
+
}
|
|
124
|
+
return buildResult(true, returnResult ? res?.data : true);
|
|
122
125
|
}
|
|
123
|
-
return buildResult(true, returnResult ? res?.data : true);
|
|
124
126
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
127
|
+
catch (attemptError) {
|
|
128
|
+
if (attemptError instanceof CircuitBreakerOpenError) {
|
|
129
|
+
throw attemptError;
|
|
130
|
+
}
|
|
131
|
+
if (circuitBreakerInstance && circuitBreakerInstance.getState().config.trackIndividualAttempts) {
|
|
132
|
+
circuitBreakerInstance.recordAttemptFailure();
|
|
133
|
+
if (circuitBreakerInstance.getState().state === CircuitBreakerState.OPEN) {
|
|
134
|
+
throw new CircuitBreakerOpenError(`${formatLogContext(executionContext)}stable-request: Circuit breaker opened after attempt ${currentAttempt}. No further retries.`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
128
137
|
throw attemptError;
|
|
129
138
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
139
|
+
const originalResOk = res.ok;
|
|
140
|
+
let performNextAttempt = false;
|
|
141
|
+
if (res.ok) {
|
|
142
|
+
try {
|
|
143
|
+
performNextAttempt = !(await executeWithPersistence(responseAnalyzer, {
|
|
144
|
+
fn,
|
|
145
|
+
args,
|
|
146
|
+
data: res?.data,
|
|
147
|
+
trialMode,
|
|
148
|
+
params: hookParams?.responseAnalyzerParams,
|
|
149
|
+
preExecutionResult,
|
|
150
|
+
commonBuffer,
|
|
151
|
+
executionContext
|
|
152
|
+
}, statePersistence, executionContext || {}, commonBuffer));
|
|
134
153
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
let performNextAttempt = false;
|
|
140
|
-
if (res.ok) {
|
|
141
|
-
try {
|
|
142
|
-
performNextAttempt = !(await executeWithPersistence(responseAnalyzer, {
|
|
143
|
-
fn,
|
|
144
|
-
args,
|
|
145
|
-
data: res?.data,
|
|
146
|
-
trialMode,
|
|
147
|
-
params: hookParams?.responseAnalyzerParams,
|
|
148
|
-
preExecutionResult,
|
|
149
|
-
commonBuffer,
|
|
150
|
-
executionContext
|
|
151
|
-
}, statePersistence, executionContext || {}, commonBuffer));
|
|
152
|
-
}
|
|
153
|
-
catch (e) {
|
|
154
|
-
console.error(`${formatLogContext(executionContext)}stable-request: Unable to analyze the response returned on attempt #${currentAttempt}. Response: ${safelyStringify(res?.data, maxSerializableChars)}`);
|
|
155
|
-
console.error(`${formatLogContext(executionContext)}stable-request: Error message provided by your responseAnalyzer: ${safelyStringify(e.message, maxSerializableChars)}`);
|
|
156
|
-
performNextAttempt = true;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
if (circuitBreakerInstance && circuitBreakerInstance.getState().config.trackIndividualAttempts) {
|
|
160
|
-
if (res.ok && !performNextAttempt) {
|
|
161
|
-
circuitBreakerInstance.recordAttemptSuccess();
|
|
162
|
-
}
|
|
163
|
-
else if (!res.ok || performNextAttempt) {
|
|
164
|
-
circuitBreakerInstance.recordAttemptFailure();
|
|
165
|
-
if (circuitBreakerInstance.getState().state === CircuitBreakerState.OPEN) {
|
|
166
|
-
throw new CircuitBreakerOpenError(`${formatLogContext(executionContext)}stable-request: Circuit breaker opened after attempt ${currentAttempt}/${maxAttempts}. Blocking further retries.`);
|
|
154
|
+
catch (e) {
|
|
155
|
+
console.error(`${formatLogContext(executionContext)}stable-request: Unable to analyze the response returned on attempt #${currentAttempt}. Response: ${safelyStringify(res?.data, maxSerializableChars)}`);
|
|
156
|
+
console.error(`${formatLogContext(executionContext)}stable-request: Error message provided by your responseAnalyzer: ${safelyStringify(e.message, maxSerializableChars)}`);
|
|
157
|
+
performNextAttempt = true;
|
|
167
158
|
}
|
|
168
159
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
errorLogs.push(errorLog);
|
|
180
|
-
try {
|
|
181
|
-
await executeWithPersistence(handleErrors, {
|
|
182
|
-
fn,
|
|
183
|
-
args,
|
|
184
|
-
errorLog,
|
|
185
|
-
maxSerializableChars,
|
|
186
|
-
params: hookParams?.handleErrorsParams,
|
|
187
|
-
preExecutionResult,
|
|
188
|
-
commonBuffer,
|
|
189
|
-
executionContext
|
|
190
|
-
}, statePersistence, executionContext || {}, commonBuffer);
|
|
191
|
-
}
|
|
192
|
-
catch (e) {
|
|
193
|
-
console.error(`${formatLogContext(executionContext)}stable-request: Unable to report errors due to issues with error handler! Error message provided by your handleErrors: ${safelyStringify(e.message, maxSerializableChars)}`);
|
|
160
|
+
if (circuitBreakerInstance && circuitBreakerInstance.getState().config.trackIndividualAttempts) {
|
|
161
|
+
if (res.ok && !performNextAttempt) {
|
|
162
|
+
circuitBreakerInstance.recordAttemptSuccess();
|
|
163
|
+
}
|
|
164
|
+
else if (!res.ok || performNextAttempt) {
|
|
165
|
+
circuitBreakerInstance.recordAttemptFailure();
|
|
166
|
+
if (circuitBreakerInstance.getState().state === CircuitBreakerState.OPEN) {
|
|
167
|
+
throw new CircuitBreakerOpenError(`${formatLogContext(executionContext)}stable-request: Circuit breaker opened after attempt ${currentAttempt}/${maxAttempts}. Blocking further retries.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
194
170
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
hadAtLeastOneSuccess = true;
|
|
198
|
-
lastSuccessfulAttemptData = res?.data;
|
|
199
|
-
successfulAttemptsCount++;
|
|
200
|
-
if (logAllSuccessfulAttempts) {
|
|
201
|
-
const successfulAttemptLog = {
|
|
202
|
-
attempt: `${currentAttempt}/${maxAttempts}`,
|
|
171
|
+
if ((!res.ok || (res.ok && performNextAttempt)) && logAllErrors) {
|
|
172
|
+
const errorLog = {
|
|
203
173
|
timestamp: res.timestamp,
|
|
204
|
-
|
|
174
|
+
attempt: `${currentAttempt}/${maxAttempts}`,
|
|
175
|
+
error: res?.error ??
|
|
176
|
+
`${formatLogContext(executionContext)}stable-request: The response did not match your expectations! Response: ${safelyStringify(res?.data, maxSerializableChars)}`,
|
|
177
|
+
isRetryable: res.isRetryable,
|
|
205
178
|
executionTime: res.executionTime
|
|
206
179
|
};
|
|
207
|
-
|
|
180
|
+
errorLogs.push(errorLog);
|
|
208
181
|
try {
|
|
209
|
-
await executeWithPersistence(
|
|
182
|
+
await executeWithPersistence(handleErrors, {
|
|
210
183
|
fn,
|
|
211
184
|
args,
|
|
212
|
-
|
|
185
|
+
errorLog,
|
|
213
186
|
maxSerializableChars,
|
|
214
|
-
params: hookParams?.
|
|
187
|
+
params: hookParams?.handleErrorsParams,
|
|
215
188
|
preExecutionResult,
|
|
216
189
|
commonBuffer,
|
|
217
190
|
executionContext
|
|
218
191
|
}, statePersistence, executionContext || {}, commonBuffer);
|
|
219
192
|
}
|
|
220
193
|
catch (e) {
|
|
221
|
-
console.error(`${formatLogContext(executionContext)}stable-request: Unable to report
|
|
194
|
+
console.error(`${formatLogContext(executionContext)}stable-request: Unable to report errors due to issues with error handler! Error message provided by your handleErrors: ${safelyStringify(e.message, maxSerializableChars)}`);
|
|
222
195
|
}
|
|
223
196
|
}
|
|
197
|
+
if (res.ok && !performNextAttempt) {
|
|
198
|
+
hadAtLeastOneSuccess = true;
|
|
199
|
+
lastSuccessfulAttemptData = res?.data;
|
|
200
|
+
successfulAttemptsCount++;
|
|
201
|
+
if (logAllSuccessfulAttempts) {
|
|
202
|
+
const successfulAttemptLog = {
|
|
203
|
+
attempt: `${currentAttempt}/${maxAttempts}`,
|
|
204
|
+
timestamp: res.timestamp,
|
|
205
|
+
data: res?.data,
|
|
206
|
+
executionTime: res.executionTime
|
|
207
|
+
};
|
|
208
|
+
successfulAttemptsList.push(successfulAttemptLog);
|
|
209
|
+
try {
|
|
210
|
+
await executeWithPersistence(handleSuccessfulAttemptData, {
|
|
211
|
+
fn,
|
|
212
|
+
args,
|
|
213
|
+
successfulAttemptData: successfulAttemptLog,
|
|
214
|
+
maxSerializableChars,
|
|
215
|
+
params: hookParams?.handleSuccessfulAttemptDataParams,
|
|
216
|
+
preExecutionResult,
|
|
217
|
+
commonBuffer,
|
|
218
|
+
executionContext
|
|
219
|
+
}, statePersistence, executionContext || {}, commonBuffer);
|
|
220
|
+
}
|
|
221
|
+
catch (e) {
|
|
222
|
+
console.error(`${formatLogContext(executionContext)}stable-request: Unable to report successful attempts due to issues with successful attempt data handler! Error message provided by your handleSuccessfulAttemptData: ${safelyStringify(e.message, maxSerializableChars)}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (performNextAttempt && res.isRetryable) {
|
|
227
|
+
res.ok = false;
|
|
228
|
+
}
|
|
229
|
+
if (attempts > 0 &&
|
|
230
|
+
((!originalResOk && res.isRetryable) ||
|
|
231
|
+
(originalResOk && performNextAttempt) ||
|
|
232
|
+
performAllAttempts)) {
|
|
233
|
+
await delay(getNewDelayTime(retryStrategy, wait, currentAttempt, jitter), maxAllowedWait);
|
|
234
|
+
}
|
|
235
|
+
} while (attempts > 0 &&
|
|
236
|
+
((res.isRetryable && !res.ok) || performAllAttempts));
|
|
237
|
+
if (performAllAttempts && hadAtLeastOneSuccess) {
|
|
238
|
+
if (trialMode.enabled) {
|
|
239
|
+
console.info(`${formatLogContext(executionContext)}stable-request: Final response (performAllAttempts mode):\n`, safelyStringify(lastSuccessfulAttemptData, maxSerializableChars));
|
|
240
|
+
}
|
|
241
|
+
return buildResult(true, returnResult ? lastSuccessfulAttemptData : true);
|
|
224
242
|
}
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
performAllAttempts)) {
|
|
232
|
-
await delay(getNewDelayTime(retryStrategy, wait, currentAttempt, jitter), maxAllowedWait);
|
|
243
|
+
else if (res.ok) {
|
|
244
|
+
if (trialMode.enabled) {
|
|
245
|
+
const finalResponse = res?.data ?? lastSuccessfulAttemptData;
|
|
246
|
+
console.info(`${formatLogContext(executionContext)}stable-request: Final response:\n`, safelyStringify(finalResponse, maxSerializableChars));
|
|
247
|
+
}
|
|
248
|
+
return buildResult(true, returnResult ? (res?.data ?? lastSuccessfulAttemptData) : true);
|
|
233
249
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
250
|
+
else {
|
|
251
|
+
throw new Error(safelyStringify({
|
|
252
|
+
error: res?.error,
|
|
253
|
+
'Function': fn.name || 'anonymous',
|
|
254
|
+
'Args': args,
|
|
255
|
+
}, maxSerializableChars));
|
|
239
256
|
}
|
|
240
|
-
return buildResult(true, returnResult ? lastSuccessfulAttemptData : true);
|
|
241
257
|
}
|
|
242
|
-
|
|
258
|
+
catch (e) {
|
|
243
259
|
if (trialMode.enabled) {
|
|
244
|
-
|
|
245
|
-
|
|
260
|
+
console.error(`${formatLogContext(executionContext)}stable-request: Final error:\n`, e.message);
|
|
261
|
+
}
|
|
262
|
+
let errorAnalysisResult = false;
|
|
263
|
+
try {
|
|
264
|
+
errorAnalysisResult = await executeWithPersistence(finalErrorAnalyzer, {
|
|
265
|
+
fn,
|
|
266
|
+
args,
|
|
267
|
+
error: e,
|
|
268
|
+
trialMode,
|
|
269
|
+
params: hookParams?.finalErrorAnalyzerParams,
|
|
270
|
+
preExecutionResult,
|
|
271
|
+
commonBuffer,
|
|
272
|
+
executionContext
|
|
273
|
+
}, statePersistence, executionContext || {}, commonBuffer);
|
|
274
|
+
}
|
|
275
|
+
catch (errorAnalysisError) {
|
|
276
|
+
console.error(`${formatLogContext(executionContext)}stable-request: Unable to analyze the final error returned. Error message provided by your finalErrorAnalyzer: ${safelyStringify(errorAnalysisError.message, maxSerializableChars)}`);
|
|
277
|
+
}
|
|
278
|
+
if (!errorAnalysisResult) {
|
|
279
|
+
throw e;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
return buildResult(false, undefined, e.message || 'Function execution failed');
|
|
246
283
|
}
|
|
247
|
-
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
try {
|
|
287
|
+
if (executionTimeout && executionTimeout > 0) {
|
|
288
|
+
return await executeWithTimeout(executeFunction(), executionTimeout, `${formatLogContext(executionContext)}stable-request: Function execution exceeded timeout of ${executionTimeout}ms`);
|
|
248
289
|
}
|
|
249
290
|
else {
|
|
250
|
-
|
|
251
|
-
error: res?.error,
|
|
252
|
-
'Function': fn.name || 'anonymous',
|
|
253
|
-
'Args': args,
|
|
254
|
-
}, maxSerializableChars));
|
|
291
|
+
return await executeFunction();
|
|
255
292
|
}
|
|
256
293
|
}
|
|
257
294
|
catch (e) {
|
|
258
|
-
if (
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
let errorAnalysisResult = false;
|
|
262
|
-
try {
|
|
263
|
-
errorAnalysisResult = await executeWithPersistence(finalErrorAnalyzer, {
|
|
264
|
-
fn,
|
|
265
|
-
args,
|
|
266
|
-
error: e,
|
|
267
|
-
trialMode,
|
|
268
|
-
params: hookParams?.finalErrorAnalyzerParams,
|
|
269
|
-
preExecutionResult,
|
|
270
|
-
commonBuffer,
|
|
271
|
-
executionContext
|
|
272
|
-
}, statePersistence, executionContext || {}, commonBuffer);
|
|
273
|
-
}
|
|
274
|
-
catch (errorAnalysisError) {
|
|
275
|
-
console.error(`${formatLogContext(executionContext)}stable-request: Unable to analyze the final error returned. Error message provided by your finalErrorAnalyzer: ${safelyStringify(errorAnalysisError.message, maxSerializableChars)}`);
|
|
276
|
-
}
|
|
277
|
-
if (!errorAnalysisResult) {
|
|
278
|
-
throw e;
|
|
279
|
-
}
|
|
280
|
-
else {
|
|
281
|
-
return buildResult(false, undefined, e.message || 'Function execution failed');
|
|
295
|
+
if (e instanceof TimeoutError) {
|
|
296
|
+
return buildResult(false, undefined, e.message);
|
|
282
297
|
}
|
|
298
|
+
throw e;
|
|
283
299
|
}
|
|
284
300
|
}
|
|
285
301
|
//# sourceMappingURL=stable-function.js.map
|