68 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			68 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import { DEFAULT_MAX_ATTEMPTS, RETRY_MODES } from "./config";
 | 
						|
import { DEFAULT_RETRY_DELAY_BASE, INITIAL_RETRY_TOKENS, NO_RETRY_INCREMENT, RETRY_COST, THROTTLING_RETRY_DELAY_BASE, TIMEOUT_RETRY_COST, } from "./constants";
 | 
						|
import { getDefaultRetryBackoffStrategy } from "./defaultRetryBackoffStrategy";
 | 
						|
import { createDefaultRetryToken } from "./defaultRetryToken";
 | 
						|
export class StandardRetryStrategy {
 | 
						|
    maxAttempts;
 | 
						|
    mode = RETRY_MODES.STANDARD;
 | 
						|
    capacity = INITIAL_RETRY_TOKENS;
 | 
						|
    retryBackoffStrategy = getDefaultRetryBackoffStrategy();
 | 
						|
    maxAttemptsProvider;
 | 
						|
    constructor(maxAttempts) {
 | 
						|
        this.maxAttempts = maxAttempts;
 | 
						|
        this.maxAttemptsProvider = typeof maxAttempts === "function" ? maxAttempts : async () => maxAttempts;
 | 
						|
    }
 | 
						|
    async acquireInitialRetryToken(retryTokenScope) {
 | 
						|
        return createDefaultRetryToken({
 | 
						|
            retryDelay: DEFAULT_RETRY_DELAY_BASE,
 | 
						|
            retryCount: 0,
 | 
						|
        });
 | 
						|
    }
 | 
						|
    async refreshRetryTokenForRetry(token, errorInfo) {
 | 
						|
        const maxAttempts = await this.getMaxAttempts();
 | 
						|
        if (this.shouldRetry(token, errorInfo, maxAttempts)) {
 | 
						|
            const errorType = errorInfo.errorType;
 | 
						|
            this.retryBackoffStrategy.setDelayBase(errorType === "THROTTLING" ? THROTTLING_RETRY_DELAY_BASE : DEFAULT_RETRY_DELAY_BASE);
 | 
						|
            const delayFromErrorType = this.retryBackoffStrategy.computeNextBackoffDelay(token.getRetryCount());
 | 
						|
            const retryDelay = errorInfo.retryAfterHint
 | 
						|
                ? Math.max(errorInfo.retryAfterHint.getTime() - Date.now() || 0, delayFromErrorType)
 | 
						|
                : delayFromErrorType;
 | 
						|
            const capacityCost = this.getCapacityCost(errorType);
 | 
						|
            this.capacity -= capacityCost;
 | 
						|
            return createDefaultRetryToken({
 | 
						|
                retryDelay,
 | 
						|
                retryCount: token.getRetryCount() + 1,
 | 
						|
                retryCost: capacityCost,
 | 
						|
            });
 | 
						|
        }
 | 
						|
        throw new Error("No retry token available");
 | 
						|
    }
 | 
						|
    recordSuccess(token) {
 | 
						|
        this.capacity = Math.max(INITIAL_RETRY_TOKENS, this.capacity + (token.getRetryCost() ?? NO_RETRY_INCREMENT));
 | 
						|
    }
 | 
						|
    getCapacity() {
 | 
						|
        return this.capacity;
 | 
						|
    }
 | 
						|
    async getMaxAttempts() {
 | 
						|
        try {
 | 
						|
            return await this.maxAttemptsProvider();
 | 
						|
        }
 | 
						|
        catch (error) {
 | 
						|
            console.warn(`Max attempts provider could not resolve. Using default of ${DEFAULT_MAX_ATTEMPTS}`);
 | 
						|
            return DEFAULT_MAX_ATTEMPTS;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    shouldRetry(tokenToRenew, errorInfo, maxAttempts) {
 | 
						|
        const attempts = tokenToRenew.getRetryCount() + 1;
 | 
						|
        return (attempts < maxAttempts &&
 | 
						|
            this.capacity >= this.getCapacityCost(errorInfo.errorType) &&
 | 
						|
            this.isRetryableError(errorInfo.errorType));
 | 
						|
    }
 | 
						|
    getCapacityCost(errorType) {
 | 
						|
        return errorType === "TRANSIENT" ? TIMEOUT_RETRY_COST : RETRY_COST;
 | 
						|
    }
 | 
						|
    isRetryableError(errorType) {
 | 
						|
        return errorType === "THROTTLING" || errorType === "TRANSIENT";
 | 
						|
    }
 | 
						|
}
 |