Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[core-rest-pipeline] Idea for improving the tokenCycler #15120

Conversation

sadasant
Copy link
Contributor

@sadasant sadasant commented May 3, 2021

This PR aims to do small improvements to the tokenCycler. The idea is that we could pick up the pace to do small improvements like this over time. Small refactorings like this should not change the behavior of our products, and can help us continue to improve how we express ideas as we spend more time working with them.

The tokenCycler was originally introduced to fix a bug in the @azure/core-http package. This package reached GA a while ago and since then it had some crucial components that had to be essentially replaced by the tokenCycler (they weren't publicly replaced, but internally they were replaced). As we wanted to fix the issue without introducing more public surface complexity, the tokenCycler became a bit its own ecosystem: No changes to the public API, and internally no reliance on existing APIs.

Later on we copied the cycler to core-rest-pipeline, and later as we introduced the challenge callbacks, it was necessary to expose some part of the cycler. Since the cycler exposes a getToken function (an async function that can receive scopes and getTokenOptions and eventually can return a token), this function became exposed to SDK client developers if they wanted to use the bearerTokenChallengePolicy. This public side of the cycler is very small, and I believe it is essentially perfect. It can't be made simpler, as it's a function that creates a getToken function.

Internally, the cycler has to deal with complexity related to:

  • Actually retrieving the token, of course.
  • How often to retry getting the token, and until when.
  • When to wait or not wait for the token to be updated.

Some of these ideas could be eventually useful for other work we may do in the SDK. For now though, I believe doing a minimal set of changes to abstract away some of the functionality will reduce the complexity of the cycler by reducing the variable behavior. Business logic anywhere tends to feel like "this is just how it needs to be" (and thus may discourage changes), so as part of this PR, I'm attempting to make some of the functions the cycler uses more like universal utilities and less like subjective, variable business logic.

This PR:

  • Reverts the useless AccessTokenRefresher interface and the associated cachedToken (I introduced this under the "CAE callbacks" PR, as an idea, then I forgot to clean this up) (I can do this in another PR if the rest of the changes aren't good for the team).
  • Replaces the tryGetAccessToken inside the cycler's beginRefresh function with a generic tryUntilTimeout<T> which calls to any asyncFn until a given timeout is reached.
  • Replaces the cycler's beginRefresh function with a generic retryUntilTimeout<T> which retries an asynchronous function as often as the provided interval milliseconds, until the provided Unix date timeout is reached.
  • Renames the internal cycler object to cyclerState, as this object represents the most stateful elements of the cycler.

Reviews appreciated 🙇‍♂️🌞

Copy link
Member

@witemple-msft witemple-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I like the idea of taking the token cycler and making a robust abstraction out of it that can be used in many different ways. I have a few comments/concerns about the proposed changes, and at a very high level I think that if we want to make this a general purpose abstraction, that we would want to re-think a few aspects of the design.

sdk/core/core-rest-pipeline/src/util/tokenCycler.ts Outdated Show resolved Hide resolved

while (token === null) {
while (result === null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue I have with this is that it's still particular to the contract of getToken in core-auth. You wouldn't be able to use this abstraction with something that doesn't return null as a failure mode. The way this reads to me is "retry every X milliseconds as long as this function is returning null," and then in the inner function we catch errors and return them as null to retry, but that might not always be safe to do in the general case.

If we want to do this refactor with the intention of making this cycler abstraction easier to use with other types of functions, then I think we have to re-think the failure modes to come up with something truly generic. For example, what if the retryUntilTimeout function accepts as a parameter a predicate that can be used to determine whether an Error can be retried or should be treated as fatal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the failure modes are an issue on themselves and that null as a failure mode is not generic, but null as a failure mode is easy for two reasons:

  • Has way less overhead.
  • It's as clear as possible on the type that uses null as the failure mode. It has been clear enough for us to use it in the TokenCredential, for example 🤔

As an alternative to null as a failure mode, what would be better while still not make it much more complicated?

sdk/core/core-rest-pipeline/src/util/tokenCycler.ts Outdated Show resolved Hide resolved
sdk/core/core-rest-pipeline/src/util/tokenCycler.ts Outdated Show resolved Hide resolved
sdk/core/core-rest-pipeline/src/util/tokenCycler.ts Outdated Show resolved Hide resolved
}

return finalToken;
async function tryUntilTimeout<T>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me the names tryUntilTimeout and retryUntilTimeout mean the same thing. This function is specifically made to be part of the inner loop of retryUntilTimeout, and I think it has limited utility on its own.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inside of retryUntilTimeout I think it adds too much complexity for one function. The name could be better though. This could become something like "run silently unless a condition is met", and receive a condition instead of a timeout, which would be generic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just think it's a very particular function. "tryOnceAndThrowIfTimeoutExpired"... It only makes sense to be used with retryUntilTimeout, which still parses in my brain as meaning the exact same thing as tryUntilTimeout. Maybe this function could simply be called tryOnce.

I generally like the idea of accepting a predicate representing the error condition instead of checking the date, but like I mentioned in the other comment, I'm skeptical that this makes much sense to use with other "refreshable things" as long as | null is baked so deeply in. That feels like a leaky abstraction where things would start wanting to return null as a failure mode just for the sake of fitting into that abstraction, even if it's more natural to reject/call a handler, etc.

It might also be possible to use some clever promise chains to eliminate this secondary function entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see! I like that. Alright, let's come back to this later when we have more time 👍

@voidfoo
Copy link

voidfoo commented Jun 11, 2021

Related #15678

openapi-sdkautomation bot pushed a commit to AzureSDKAutomation/azure-sdk-for-js that referenced this pull request Aug 26, 2021
Mitryakh/network march (Azure#15709)

* Adds base for updating Microsoft.Network from version stable/2021-02-01 to version 2021-03-01

* Updates readme

* Updates API version in new specs and examples

* Add new parameter to enable/disable BGP route translation for NAT VirtualWan VpnGateway (Azure#14462)

* Adding Bastion Host Configuration Features (Azure#14442)

Co-authored-by: Abhishek Shah <shabhis@microsoft.com>

* Add PL ASG (Azure#14620)

* Add PL ASG

* Prettier

* [Private Link] Private Endpoint Ipconfigurations swagger for 2021-03-01 (Azure#14662)

* Ipconfigurations swagger

* Ran prettier check

* fixing conflicting files

* fixing lint

* Fixing linter diff

* Lint diff

* resolving comments

* fixing conflict

* Fioxing

* running prettier

* Fixing linter comment

Co-authored-by: Shane Baden <shbaden@microsoft.com>

* Add missing properties of ServiceEndpointPolicy (Azure#14948)

* Add missing 'type' and 'serviceAlias' for ServiceEndpointPolicy related API response

* Add missing 'contextualServiceEndpointPolicies'

* fix bug

* fix type

* Add missing properties to ServiceEndpointPolicy

* Revert "fix type"

This reverts commit 3a49beb66d9bbb288fa0d3baaeaf5aaf2bbc6ee9.

* Revert "fix bug"

This reverts commit a4fff4ae925b01ed0cfb07b1594fb95e2c6649c7.

* Revert "Add missing 'contextualServiceEndpointPolicies'"

This reverts commit ccf19167abc61b90bce6b5204dc3b2a64fdf4ca4.

* Revert "Add missing 'type' and 'serviceAlias' for ServiceEndpointPolicy related API response"

This reverts commit e532cc1118241a74902e4362c32c25c11a764933.

Co-authored-by: Xu Wang <wax@microsoft.com>

* add new service tag details api (Azure#14958)

* add new service tag details api

* prettier check fixes

* fixing lint issue, additional properties issue

* Add Load distribution policy and global configuration to AppGW (Azure#14790)

* add Load distribution policy and global configuration to Application Gateway resource

* prettify

* remove provisioning state

* add public ssh key to network virtual appliance (Azure#14988)

* Changes needed for BgpEndpoint (Azure#14800)

* changes needed for BgpEndpoint

* prettier check

* model validation fix

* fix description

* update again

* Multi QoS support for DSCP configuration (Azure#15120)

* Updating the DSCP jsons to reflect the  multi-qos support

* Adding support to multi-qos

* adding the type object to NrpQos

* fixing the prettier
removing my update to the settings

* fixing the missing type object error. seeing if this also fixes the additional attributes exception

* fixing the prettier exception

* almost forgot this, it was refactor from NrpQos to QosDefinition. NRPQos is already being used in Networking repo

* copying what I did with my other objects

* names didn't match with what I have in NRP's src

* forgot to update the examples with the updates in the specification. updating it

* fixing the merge exceptions

* Add SQL Setting to Firewall Policy (Azure#15110)

* Add SQL Setting to Firewall Policy

* fix lint check except for object type

* resolve linter error

* network: fix newly added targets field to an array and missing sub-resource properties in applicationGateways.json (Azure#15318)

* fix: change target to an array and add subresource properties

* fix target examples

* Support to update tags for BastionHost resource (Azure#15446)

* Initial commit

* Validation fixes

* Update bastionHost.json

Co-authored-by: Abhishek Shah <shabhis@microsoft.com>

* Azure FW - Explicit Proxy feature swagger change (Azure#15017)

* explicit proxy swagger change

* prettier check fix

* lint check

* prettier fix

* revert unnecessary file change

* Add kind to virtual hub (Azure#15562)

* Adding customnetworkinterfacename attribute to Private Endpoint (Azure#15574)

Co-authored-by: Shane Baden <shbaden@microsoft.com>

* Added 3 new properties to InboundNatRule resource (Azure#15611)

* Added the missins properties to fix the change breaking update (Azure#15732)

Co-authored-by: Nilambari <nilamd@microsoft.com>
Co-authored-by: Abhishek Shah <shah.abhi7860@gmail.com>
Co-authored-by: Abhishek Shah <shabhis@microsoft.com>
Co-authored-by: Yang Shi <yangshi93@gmail.com>
Co-authored-by: Shane Baden <naruto.shane@gmail.com>
Co-authored-by: Shane Baden <shbaden@microsoft.com>
Co-authored-by: Xu Wang <wangxu724@gmail.com>
Co-authored-by: Xu Wang <wax@microsoft.com>
Co-authored-by: guptas14 <71726901+guptas14@users.noreply.github.com>
Co-authored-by: Akshay Gupta <aksgupta@microsoft.com>
Co-authored-by: litchiyangMSFT <64560090+litchiyangMSFT@users.noreply.github.com>
Co-authored-by: Ritvika Reddy Nagula <rinagula@microsoft.com>
Co-authored-by: David Velasco <davelasc@microsoft.com>
Co-authored-by: Jiejiong Wu <b564518128@users.noreply.github.com>
Co-authored-by: tinawu6 <78238424+tinawu6@users.noreply.github.com>
Co-authored-by: irrogozh <irrogozh@microsoft.com>
@ghost ghost added the no-recent-activity There has been no recent activity on this issue. label Feb 25, 2022
@ghost
Copy link

ghost commented Feb 25, 2022

Hi @sadasant. Thank you for your interest in helping to improve the Azure SDK experience and for your contribution. We've noticed that there hasn't been recent engagement on this pull request. If this is still an active work stream, please let us know by pushing some changes or leaving a comment. Otherwise, we'll close this out in 7 days.

@sadasant sadasant closed this Feb 25, 2022
@sadasant sadasant deleted the core-rest-pipeline/tokenCyclerImprovementIdea branch February 25, 2022 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Azure.Core no-recent-activity There has been no recent activity on this issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants