Skip to content

Commit

Permalink
move system clock offset check to AWS SDK
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhe committed Feb 27, 2024
1 parent e5c9eeb commit c1a611a
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 37 deletions.
2 changes: 0 additions & 2 deletions packages/middleware-retry/src/configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ export interface PreviouslyResolved {
* @internal
*/
retryMode: string | Provider<string>;

systemClockOffset?: number;
}

/**
Expand Down
3 changes: 3 additions & 0 deletions packages/middleware-retry/src/retryDecider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import {
} from "@smithy/service-error-classification";
import { SdkError } from "@smithy/types";

/**
* @deprecated this is only used in the deprecated StandardRetryStrategy. Do not use in new code.
*/
export const defaultRetryDecider = (error: SdkError) => {
if (!error) {
return false;
Expand Down
28 changes: 7 additions & 21 deletions packages/middleware-retry/src/retryMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,15 @@ import {
RetryStrategyV2,
RetryToken,
SdkError,
SkdErrorWithClockSkewMetadata,
} from "@smithy/types";
import { INVOCATION_ID_HEADER, REQUEST_HEADER } from "@smithy/util-retry";
import { v4 } from "uuid";

import { PreviouslyResolved, RetryResolvedConfig } from "./configurations";
import { RetryResolvedConfig } from "./configurations";
import { isStreamingPayload } from "./isStreamingPayload/isStreamingPayload";
import { asSdkError } from "./util";

export const retryMiddleware = (options: RetryResolvedConfig & Partial<PreviouslyResolved>) => <
Output extends MetadataBearer = MetadataBearer
>(
export const retryMiddleware = (options: RetryResolvedConfig) => <Output extends MetadataBearer = MetadataBearer>(
next: FinalizeHandler<any, Output>,
context: HandlerExecutionContext
): FinalizeHandler<any, Output> => async (
Expand All @@ -49,30 +46,19 @@ export const retryMiddleware = (options: RetryResolvedConfig & Partial<Previousl
request.headers[INVOCATION_ID_HEADER] = v4();
}

let initialSystemClockOffset = 0;

while (true) {
try {
if (isRequest) {
request.headers[REQUEST_HEADER] = `attempt=${attempts + 1}; max=${maxAttempts}`;
}
initialSystemClockOffset = options.systemClockOffset ?? 0 | 0;
const { response, output } = await next(args);
retryStrategy.recordSuccess(retryToken);
output.$metadata.attempts = attempts + 1;
output.$metadata.totalRetryDelay = totalRetryDelay;
return { response, output };
} catch (e: unknown) {
const latestSystemClockOffset = options.systemClockOffset ?? 0 | 0;
const clockSkewCorrected = initialSystemClockOffset !== latestSystemClockOffset;

const sdkError = e as SkdErrorWithClockSkewMetadata;
if (clockSkewCorrected && sdkError.$metadata && typeof sdkError.$metadata === "object") {
sdkError.$metadata.clockSkewCorrected = true;
}

const retryErrorInfo = getRetryErrorInfo(sdkError);
lastError = asSdkError(sdkError);
} catch (e: any) {
const retryErrorInfo = getRetryErrorInfo(e);
lastError = asSdkError(e);

if (isRequest && isStreamingPayload(request)) {
(context.logger instanceof NoOpLogger ? console : context.logger)?.warn(
Expand Down Expand Up @@ -110,7 +96,7 @@ const isRetryStrategyV2 = (retryStrategy: RetryStrategy | RetryStrategyV2) =>
typeof (retryStrategy as RetryStrategyV2).refreshRetryTokenForRetry !== "undefined" &&
typeof (retryStrategy as RetryStrategyV2).recordSuccess !== "undefined";

const getRetryErrorInfo = (error: SkdErrorWithClockSkewMetadata): RetryErrorInfo => {
const getRetryErrorInfo = (error: SdkError): RetryErrorInfo => {
const errorInfo: RetryErrorInfo = {
error,
errorType: getRetryErrorType(error),
Expand All @@ -122,7 +108,7 @@ const getRetryErrorInfo = (error: SkdErrorWithClockSkewMetadata): RetryErrorInfo
return errorInfo;
};

const getRetryErrorType = (error: SkdErrorWithClockSkewMetadata): RetryErrorType => {
const getRetryErrorType = (error: SdkError): RetryErrorType => {
if (isThrottlingError(error)) return "THROTTLING";
if (isTransientError(error)) return "TRANSIENT";
if (isServerError(error)) return "SERVER_ERROR";
Expand Down
14 changes: 11 additions & 3 deletions packages/service-error-classification/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SdkError, SkdErrorWithClockSkewMetadata } from "@smithy/types";
import { SdkError } from "@smithy/types";

import {
CLOCK_SKEW_ERROR_CODES,
Expand All @@ -10,8 +10,16 @@ import {

export const isRetryableByTrait = (error: SdkError) => error.$retryable !== undefined;

/**
* @deprecated use isClockSkewCorrectedError. This is only used in deprecated code.
*/
export const isClockSkewError = (error: SdkError) => CLOCK_SKEW_ERROR_CODES.includes(error.name);

/**
* @returns whether the error resulted in a systemClockOffset aka clock skew correction.
*/
export const isClockSkewCorrectedError = (error: SdkError) => error.$metadata?.clockSkewCorrected;

export const isThrottlingError = (error: SdkError) =>
error.$metadata?.httpStatusCode === 429 ||
THROTTLING_ERROR_CODES.includes(error.name) ||
Expand All @@ -23,8 +31,8 @@ export const isThrottlingError = (error: SdkError) =>
* cause where the NodeHttpHandler does not decorate the Error with
* the name "TimeoutError" to be checked by the TRANSIENT_ERROR_CODES condition.
*/
export const isTransientError = (error: SdkError | SkdErrorWithClockSkewMetadata) =>
(error as SkdErrorWithClockSkewMetadata).$metadata?.clockSkewCorrected ||
export const isTransientError = (error: SdkError) =>
isClockSkewCorrectedError(error) ||
TRANSIENT_ERROR_CODES.includes(error.name) ||
NODEJS_TIMEOUT_ERROR_CODES.includes((error as { code?: string })?.code || "") ||
TRANSIENT_ERROR_STATUS_CODES.includes(error.$metadata?.httpStatusCode || 0);
Expand Down
22 changes: 11 additions & 11 deletions packages/types/src/shapes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@ export interface SmithyException {
* the base exception for the service should be used. Each client exports
* a base ServiceException prefixed with the service name.
*/
export type SdkError = Error & Partial<SmithyException> & Partial<MetadataBearer>;

/**
* @internal
*
* @deprecated for same reason as SdkError. Use public client modeled exceptions in application code.
*/
export type SkdErrorWithClockSkewMetadata = SdkError & {
$metadata: SdkError["$metadata"] & {
clockSkewCorrected?: boolean;
export type SdkError = Error &
Partial<SmithyException> &
Partial<MetadataBearer> & {
$metadata?: Partial<MetadataBearer>["$metadata"] & {
/**
* If present, will have value of true and indicates that the error resulted in a
* correction of the clock skew, a.k.a. config.systemClockOffset.
* This is specific to AWS SDK and sigv4.
*/
readonly clockSkewCorrected?: true;
};
};
};

0 comments on commit c1a611a

Please sign in to comment.