Closed unchartedxx closed 5 months ago
@unchartedxx Good morning. I understand that the call to UserCrypto.IsUserCryptAvailable
might be calling few milliseconds of delay (140ms), could you please share the statistics when disabling use of UserCrypto
as assumed in the code? Is it substantially less than 200ms. Kindly note that there is an existing issue https://github.com/aws/aws-sdk-net/issues/1931 to address it on non-Windows environment, but there is no specific timeline yet to address it.
Thanks, Ashish
@ashishdhingra thanks for the quick answer. I did some more tests hoping that it would clarify your doubts.
I redid the test using the official AWS .NET lambda blueprints from here. I only added code to measure the impact of the UserCrypto.IsUserCryptAvailable
by calling it before the client initialization.
I tested with .NET 6 using the runtime provided by the lambda service and also using the custom runtime using .NET 7 in AOT mode. The Lambda function was assigned with 256MB. Each situation was tested three times, each time forcing a cold start. All times in milliseconds.
.NET 6 Only Client Initialization
Client Init |
---|
322 |
358 |
341 |
.NET 6 Calling UserCrypto.IsUserCryptAvailable
and then Client Initialization
IsUserCryptAvailable | Client Init | Total |
---|---|---|
80 | 279 | 359 |
80 | 279 | 359 |
98 | 243 | 341 |
.NET 7 AOT Only Client Initialization
Client Init |
---|
180 181 219
.NET 7 AOT Calling UserCrypto.IsUserCryptAvailable
and then Client Initialization
IsUserCryptAvailable | Client Init | Total |
---|---|---|
160 | 20 | 180 |
158 | 22 | 180 |
159 | 20 | 179 |
It is possible to see that if the UserCrypto.IsUserCryptAvailable
was a no-op it would exactly remove a chunk from the whole time dedicated to the initialization, otherwise the total time would not the same as the client init wihout "prewarming" the crypto. In case of .NET 6 it could save about 25% of the whole time, while on .NET 7 it is about 88%, so the large majority.
Here the whole code I used for reference:
public static string FunctionHandler(string input, ILambdaContext context)
{
AWSConfigs.LoggingConfig.LogTo = LoggingOptions.Console;
Stopwatch timer = new();
timer.Start();
if (Environment.GetEnvironmentVariable("USERCRYPTO_WARMUP") != null)
{
var value = UserCrypto.IsUserCryptAvailable;
Console.WriteLine($"IsUserCryptAvailable took {timer.ElapsedMilliseconds} to get {value}");
timer.Restart();
}
var client = new AmazonLambdaClient();
Console.WriteLine($"AmazonLambdaClient took {timer.ElapsedMilliseconds} to get {client}");
return input.ToUpper();
}
We just released an update (included in Core 3.7.303.23
) where the SDK won't attempt to load Crypt32.dll
when not running on Windows.
It does require targeting .NET 8 (or greater), but should help with the latency you were seeing.
Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.
Describe the bug
I'm using the AWS SDK to build a lambda function on .NET 7 in AOT mode. I noticed that during Dependency Injection the creation of AmazonLambdaConfig and AmazonLambdaClient objects was taking the majority of the time of the whole startup. I enabled the SDK logs (AWSConfigs.LoggingConfig.LogTo = LoggingOptions.Console;) and I noticed the following messages (emphasis on the second line):
That UserCrypto message is generated at IsUserCryptAvailable. That method is called whenever downstream it needs to try to get profile information if possible in CredentialProfileStoreChain. The get profile method is called indirectly whenever it needs to find credentials or profile configuration such as this or even this.
I want to note that all of this is not specific to the AmazonLambdaClient so I suppose it would affect all AWSSDK service clients.
Normally creating the Config and the Client without doing anything spacial takes between 150 and 200ms. If I call UserCrypto.IsUserCryptoAvailable in my code it takes around ~140 ms. If I create Config and Client after that it takes a few tens of milliseconds to do the initialisation. Therefore I can conclude that the single call to UserCrypto is taking the majority of the time even considering all the time to obtain credentials following the chain of folders, envvars,...
The test was done on .NET 7 and AOT. I can only assume it would take much more on earlier versions and/or non-AOT mode. The lambda was set with 256MB.
Expected Behavior
The AWS SDK notices it is running on a non-Windows enviroment and just return false to "UserCrypto.IsUserCryptAvailable" doesn't even try to call UserCrypto API and fail when the dll are not there.
Current Behavior
"UserCrypto.IsUserCryptAvailable" calls "Decrypt" that calls "CryptProtectData" that is a Windows only API but it tries anyway for the presence of the Crypt32.dll.so file and then fails.
Reproduction Steps
AWSConfigs.LoggingConfig.LogTo = LoggingOptions.Console; var clientConfig = new AmazonLambdaConfig(); var client = new AmazonLambdaClient(clientConfig);
Possible Solution
Checks for current operating system in "UserCrypto.IsUserCryptAvailable". Return false if not Windows.
Additional Information/Context
The only workaround ATM to avoid any possible call to IsUserCryptoAvailable is to set a lot of stuff ahead of time before creating the ClientConfig and the ServiceConfig:
AWS .NET SDK and/or Package version used
AWSSDK.Lambda 3.7.104.7
Targeted .NET Platform
.NET 7 (AOT)
Operating System and version
Lambda (Amazon Linux 2)