feat(util-waiter): add waiter utilities package#1736
feat(util-waiter): add waiter utilities package#1736alexforsyth merged 15 commits intoaws:masterfrom
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 <16024985+trivikr@users.noreply.github.com>
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.
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.
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.
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.
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.
I dont think Javascript has number overflow: https://www.algotech.solutions/blog/javascript/handle-number-overflow-javascript/
There was a problem hiding this comment.
@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.
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.
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.
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.
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.
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.