Skip to content

Make DefaultInstanceProfileAWSCredentials thread safe #3803

Open
@yserious

Description

@yserious

Describe the feature

I see a lot of multi-threading-problems in the code. (I (also) get a headache looking at multi-threading, so I may be wrong at some points, but I definitely think it should be looked at!)
FWIW: I logged them as bug first (#3708), but @ashishdhingra considers it a feature request.
(Most of these issues have been copied to version 4 in 978251f)

DefaultInstanceProfileAWSCredentials.GetCredentials: await WriteLock if ReadLock times out
The ‘Failed-instance-error’ implies the 'local' credentials remain null. This seems to imply that the ReadLock either finds no credentials or times out and the WriteLock times out.
It seems very strange to try an exclusive WriteLock in case your non-exclusive ReadLock times out - what are the odds you will acquire a stricter lock now?

DefaultInstanceProfileAWSCredentials.GetCredentials: write in ReadLock
It seems very strange to write the _lastRetrievedCredentials within the ReadLock!
(introduced in ce982b0)

DefaultInstanceProfileAWSCredentials.GetCredentials: do not use locks, but do use volatile
It seems 'heavy' to block all requests with a ReadLock (every requests has to get credentials) for a WriteLock further down to renew credentials. Can't you just return the known credentials, even if they may be expired?
It seems possible to prevent a race condition by just setting a reference in a local variable (credentials = _lastRetrievedCredentials) and continue using that, especially if you make _lastRetrievedCredentials volatile and/or static.

DefaultInstanceProfileAWSCredentials.RenewCredentials: always refresh
Every time RenewCredentials is triggered by the timer, it will (try to) fetch new credentials, even if the present credentials are still far from expired.
This seems unnecessary.

DefaultInstanceProfileAWSCredentials.RenewCredentials: read and write outside lock
RenewCredentials will always read and write the credentials, outside the lock used by GetCredentials. (Introduced for GetCredentials by f75a608 )

DefaultInstanceProfileAWSCredentials-constructor RenewCredentials awaits thread
The DefaultInstanceProfileAWSCredentials-constructor tries to initialize the credentials by initializing the timer to run at 0 minutes. However, this is not immediate, it will have to wait for an available thread.
This means that all present requests (e.g. waiting due to an app pool recycle) may enter GetCredentials before RenewCredentials was actually called.
It might be better to try to first call it from the constructor-thread.

DefaultInstanceProfileAWSCredentials.Dispose: not disposed
It seems that DefaultInstanceProfileAWSCredentials is the only IDisposable AWSCredentials-implementor. It is instantiated by in FallbackCredentialsFactory.ContainerEC2CredentialsWrapper(), but never disposed.
Because DefaultInstanceProfileAWSCredentials seems a singleton, this probably has little consequences (but it's not very pretty either).

DefaultInstanceProfileAWSCredentials.Dispose: non volatile/static properties
I wonder if _lastRetrievedCredentials, _credentialsLock and _credentialsRetrieverTimer should/could not be volatile and/or static (but I do not think that is the cause of the current problems).

DefaultInstanceProfileAWSCredentials.GetCredentialAsync: not async (version 3.x)
GetCredentialAsync calls GetCredential in an extra Task.Run, so instead of being async (freeing thread resources), it seems to claim an extra thread?
It would be nice to have it implemented as async all the way, but as it a simple asynchronous façade does not yield any scalability benefits.
So right now, I think should have a synchronous signature for better readability/understanding.

Use Case

Every scenario that uses EC2 Instance credentials.

Proposed Solution

Make DefaultInstanceProfileAWSCredentials thread safe

Other Information

No response

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

AWS .NET SDK and/or Package version used

AWSSDK.S3 version 3.7.100.6 (implicitly using AWSSDK.Core version 3.7.100.6)
and AWSSDK.S3 Version=3.7.412.5 (implicitly using AWSSDK.Core version 3.7.401.5)
and AWSSDK.S3 Version=4.0.0.3 (implicitly using AWSSDK.Core version 4.0.0.3)

Targeted .NET Platform

.NET 8

Operating System and version

Window Server 2019 Datacenter

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugThis issue is a bug.credentialsp2This is a standard priority issuequeued

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions