-
Notifications
You must be signed in to change notification settings - Fork 580
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
feat(util-waiter): add waiter utilities package #1736
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1736 +/- ##
==========================================
+ Coverage 79.77% 79.83% +0.05%
==========================================
Files 325 336 +11
Lines 12087 12623 +536
Branches 2553 2679 +126
==========================================
+ Hits 9643 10078 +435
- Misses 2444 2545 +101
Continue to review full report at Codecov.
|
Co-authored-by: Trivikram Kamat <[email protected]>
36310d3
to
88ac270
Compare
|
||
export const waiterTimeout = async (seconds: number): Promise<WaiterResult> => { | ||
await sleep(seconds); | ||
return { state: WaiterState.RETRY }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be a timeout right? Since you added an ABORTED state, did you consider a TIMEOUT state?
import { WaiterOptions, WaiterResult, WaiterState } from "./waiter"; | ||
|
||
/** | ||
* Reference: https://github.com/awslabs/smithy/pull/656 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better to link to the waiters spec rather than the PR to update the spec. Maybe link here since this link will be relevant when we release the next version of Smithy that includes jitter in waiters: https://awslabs.github.io/smithy/1.0/spec/waiters.html#waiter-retries
* `maxWaitTime` | ||
*/ | ||
const exponentialBackoffWithJitter = (floor: number, ciel: number, attempt: number) => | ||
Math.floor(Math.min(ciel, randomInRange(floor, floor * 2 ** (attempt - 1)))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
floor * 2 ** (attempt - 1)
I think this will overflow your number type for large values of attempt. To address this, you can compute the maximum attempt ceiling that doesn't exceed your ceiling.
Pseudo code:
attemptCeiling = (log(maxDelay / minDelay) / log(2)) + 1
if attempt > attemptCeiling:
delay = maxDelay
else:
delay = minDelay * 2 ** (attempt - 1)
delay = random(minDelay, delay)
if remainingTime - delay <= minDelay:
delay = remainingTime - minDelay
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned in the comment, this is not possible because it is in fact limited by maxWaitTime
in the promise race in other abstraction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think Javascript has number overflow: https://www.algotech.solutions/blog/javascript/handle-number-overflow-javascript/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alexforsyth It's possible to overflow the JS Number:
> Number.MAX_VALUE * 2
> Infinity
export interface WaiterConfiguration { | ||
/** | ||
* The amount of time in seconds a user is willing to wait for a waiter to complete. This | ||
* defaults to 300 (5 minutes). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't recommend setting a default value for this. This is hands-down the most important waiter setting there is, and customers should tell us how long they're willing to wait.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh this is actually old documentation. We dont set this anywhere. I'll change
while (true) { | ||
await sleep(exponentialBackoffWithJitter(minDelay, maxDelay, currentAttempt)); | ||
const { state } = await acceptorChecks(client, input); | ||
if (state === WaiterState.SUCCESS || state === WaiterState.FAILURE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably better to say if (state !== WaiterState.RETRY) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000)); | ||
}; | ||
|
||
export const waiterTimeout = async (seconds: number): Promise<WaiterResult> => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method and abortTimeout seem like they're not really useful on their own and publicly expose implementation details of waiters. Did you consider maybe moving these into poller as non-exported functions? You could, for example, export a function like createWaiter
or something that takes the maxWaittime, abortController, waiter settings, etc, and it returns a "waiter" or whatever the abstraction is (a promise I guess), that automatically hooks up the promise race to timeout a waiter and checking the abort controller. I suspect this will make codegen simpler and limit the amount of public API surface area y'all have to maintain.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I thought about just exposing the race. This gets kinda messy cause you have to deal with the generics exposed in the client waiter and pass them all through twice. Once to createWaiter and once again through to the underlying clientWaiter. The function def gets silly.
My 2c is that this is probably too much abstraction and the code starts to be much less clean, but I can totally see what you're saying and I agree that it does reduce API surface area. @AllanZhengYP what do you think?
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs and link to relevant comments in this thread. |
Issue #, if available:
Description of changes:
Example of how clients may use these utils:
Smithy PR (WIP): smithy-lang/smithy-typescript#243
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.