Give iamSigner pointer receivers so the email cache works - #778
Open
omlahore wants to merge 2 commits into
Open
Conversation
Email() checks s.serviceAcct for a cached value, takes the mutex, and assigns the discovered account back. With a value receiver that write lands on a copy, so the cache never fills and every Email() call makes a fresh HTTP request to the metadata server. Sign() calls Email(), so this is once per custom token. The struct holds mutex as a *sync.Mutex, which only makes sense if the signer is meant to be shared and mutated, and newIAMSigner already returns *iamSigner. Nothing constructs or stores a bare iamSigner value.
Contributor
There was a problem hiding this comment.
Code Review
This pull request updates the receiver type of iamSigner methods in auth/token_generator.go from value to pointer receivers. The review feedback highlights that this change introduces a data race and a concurrency bug in the Email method due to unprotected concurrent access to s.serviceAcct, and suggests acquiring the lock at the beginning of the method to ensure thread safety and prevent redundant metadata service calls.
Moving to pointer receivers made serviceAcct genuinely shared, so the unlocked read at the top of Email raced the locked write at the bottom. It also let every concurrent caller past the empty check, so each one queried the metadata server in turn. Reading under the same lock fixes both. Added a test that runs 50 concurrent Email calls and asserts exactly one metadata request; under -race it reports the data race and 50 requests without this change.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
iamSigner.Emailis written to discover the service account once and cache it:The receiver is a value, so
s.serviceAcct = resultwrites to a copy that is discarded when the method returns. The guard at the top therefore never sees a cached value, and every call re-queries the GCE metadata server over HTTP.SigncallsEmail, so that is an extra round trip per custom token minted through the IAM path.Two things say caching is the intent rather than the value receiver being deliberate: the early-return check exists at all, and
mutexis held as a*sync.Mutexso that copies of the struct still share one lock. Guarding a write that cannot persist is only explicable as an oversight.staticcheck reports it as SA4005, "ineffective assignment to field iamSigner.serviceAcct".
The change moves all four
iamSignermethods to pointer receivers together, so the method set stays consistent.newIAMSigneralready returns*iamSigner,newCryptoSignerpasses that straight through, andauth_test.go:129assertsclient.signer.(*iamSigner), so nothing stores a bareiamSignervalue and thecryptoSignerinterface is still satisfied.Verified:
go build ./...passes,go test ./auth/...green,gofmt -lclean, SA4005 gone underGOOS=linuxandGOOS=darwin.Separately, and not included here since it is a different concern:
auth/user_mgt.go:1463-1464andauth/project_config_mgt.go:29spell the struct tag optionomitEmpty, whichencoding/jsondoes not recognise (it only acceptsomitempty). Both structs are decode-only today so nothing misbehaves at runtime, but the tags do not do what they read as. Say the word and it goes in a separate PR.