DEV Community

Toshimitsu Takahashi
Toshimitsu Takahashi

Posted on

Optimizing Credential Configuration in AWS SDK for JavaScript v3: Understanding Cache Mechanisms and Best Practices

This article is a translation of https://zenn.dev/tilfin/articles/56f8dc56b83901.

Background

The AWS SDK for JavaScript v3 introduced a significant shift in client configuration compared to v2. While v2 allowed for global SDK settings, v3 adopts a per-client instance runtime configuration approach. Consequently, credential retrieval, which was internally cached in v2 even with randomly generated clients for each AWS resource, now presents potential challenges in v3 with frequent credential requests.

Delving into Credential Retrieval Logic

The simplest approach involves directly setting AWS access and secret keys as credentials in the constructor arguments of AWS resource clients. However, this method is not ideal for real-world application development. Applications deployed on EC2 or ECS typically retrieve credentials from instance or container metadata services, while Lambda functions often obtain them via environment variables. The AWS SDK v3 implements these credential supply logics as Credential Providers.

You can also specify a Credential Provider for the client's credentials. Details are described on the following page:
https://github.com/aws/aws-sdk-js-v3/tree/main/packages/credential-providers

If nothing is specified for credentials, the function set in credentialDefaultProvider is executed first to generate a Credential Provider, which is then used as credentials. The default credentialDefaultProvider is defaultProvider. It attempts various acquisition methods. By using this method, you can automatically refer to ~/.aws/credentials in a local environment and metadata in a deployed environment.

To explicitly specify the implementation of defaultProvider, use fromNodeProviderChain.

Credential Caching Mechanism

Credential retrieval occurs each time a client invokes a command. However, Credential Providers, which implement the various retrieval logics provided by the AWS SDK, do not have a caching mechanism. The only exception is the memoize function of @smithy/property-provider used internally by defaultProvider. Furthermore, the re-acquisition logic for time-limited credentials, including session tokens, is also implemented only here.

https://github.com/aws/aws-sdk-js-v3/blob/main/packages/credential-provider-node/src/defaultProvider.ts#L59

https://github.com/smithy-lang/smithy-typescript/blob/main/packages/property-provider/src/memoize.ts#L46

What are the Best Practices?

Based on the insights so far, in most cases where you are implementing an application that operates within the same AWS account, "using defaultProvider", or "specifying nothing", is the best approach. However, there is a potential pitfall here.

As mentioned at the beginning, in v3, since each client has a different Credential Provider, caching is also done on a per-client basis. For example, in a batch processing loop where the code "creates a client and retrieves an item", there is a possibility that credential acquisition will fail. Below is an issue where an error occurred because the access limit of EC2's metadata endpoint was reached.

https://github.com/aws/aws-sdk-js-v3/issues/4867

As a workaround, the issue's comments suggest caching (reusing) the instances of each AWS client themselves. However, in applications that rely on many AWS services and where command transmissions to them occur intensively, this may not be avoidable. There is also an improvement proposal for this.

https://github.com/aws/aws-sdk-js-v3/issues/4612

Conclusion

Currently, the best practice is to reuse defaultProvider. Since fromNodeProviderChain is provided by the SDK, generate it at the module level as shown below and reuse it.

import { fromNodeProviderChain } from "@aws-sdk/credential-providers";

export const credentialProvider = fromNodeProviderChain();
Enter fullscreen mode Exit fullscreen mode
new S3Client({ credentials: credentialProvider });

new DynamoDBClient({ credentials: credentialProvider });
Enter fullscreen mode Exit fullscreen mode

This will prevent the retrieval process from being concentrated internally.

References

https://zenn.dev/luma/articles/bd3c59b3d7682d

Top comments (0)