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

feat(util-waiter): add waiter utilities package #1736

Merged
merged 15 commits into from
Dec 8, 2020

Conversation

alexforsyth
Copy link
Contributor

@alexforsyth alexforsyth commented Dec 1, 2020

Issue #, if available:

  • Adding waiter support for JS SDK V3. This is a collection of utility functions that make up the meat and bones of JS SDK waiters.
  • Internal design feedback
  • smithy-typescript codegen PR.
  • Unit Tests

Description of changes:
Example of how clients may use these utils:

const waitForTableExists = async (params: DynamoDBWaiter, input: DescribeTableInput): Promise<WaiterResult> => {
  params = {
    ...waiterServiceDefaults,
    ...params
  };
  validateWaiterOptions(params);

  const exitConditions = [
    waiterTimeout(params.maxWaitTime), 
    runPolling<DynamoDBClient, DescribeTableInput> (params, params.client, input, checkState)
  ];
  if(params.abortController){
    exitConditions.push(abortTimeout(params.abortController.signal));
  }
  return Promise.race(exitConditions);
}

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.

@alexforsyth alexforsyth changed the title WIP: JS SDK waiters in V3 JS SDK waiters in V3 Dec 3, 2020
@aws aws deleted a comment from codecov-io Dec 3, 2020
@alexforsyth alexforsyth changed the title JS SDK waiters in V3 feat(clients): waiters in JS SDK V3 Dec 4, 2020
@trivikr trivikr changed the title feat(clients): waiters in JS SDK V3 feat(util-waiter): waiters in JS SDK V3 Dec 4, 2020
@alexforsyth alexforsyth changed the title feat(util-waiter): waiters in JS SDK V3 feat(util-waiter): adding waiter utilities package Dec 4, 2020
@codecov-io
Copy link

codecov-io commented Dec 4, 2020

Codecov Report

Merging #1736 (88ac270) into master (de75f7e) will increase coverage by 0.05%.
The diff coverage is n/a.

Impacted file tree graph

@@            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     
Impacted Files Coverage Δ
...tocol_tests/aws-restxml/commands/XmlMapsCommand.ts 95.65% <0.00%> (-4.35%) ⬇️
...ocol_tests/aws-restxml/commands/XmlBlobsCommand.ts 95.65% <0.00%> (-4.35%) ⬇️
...ocol_tests/aws-restxml/commands/XmlEnumsCommand.ts 95.65% <0.00%> (-4.35%) ⬇️
...ocol_tests/aws-restxml/commands/XmlListsCommand.ts 95.65% <0.00%> (-4.35%) ⬇️
...col_tests/aws-restjson/commands/JsonMapsCommand.ts 95.65% <0.00%> (-4.35%) ⬇️
...ol_tests/aws-restjson/commands/JsonBlobsCommand.ts 95.65% <0.00%> (-4.35%) ⬇️
...ol_tests/aws-restjson/commands/JsonEnumsCommand.ts 95.65% <0.00%> (-4.35%) ⬇️
...ol_tests/aws-restjson/commands/JsonListsCommand.ts 95.65% <0.00%> (-4.35%) ⬇️
...tests/aws-restxml/commands/XmlAttributesCommand.ts 95.65% <0.00%> (-4.35%) ⬇️
...tests/aws-restxml/commands/XmlNamespacesCommand.ts 95.65% <0.00%> (-4.35%) ⬇️
... and 133 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fab1c1f...88ac270. Read the comment docs.

@trivikr trivikr changed the title feat(util-waiter): adding waiter utilities package feat(util-waiter): add waiter utilities package Dec 4, 2020
packages/util-waiter/src/poller.ts Outdated Show resolved Hide resolved
packages/util-waiter/src/poller.ts Outdated Show resolved Hide resolved
packages/util-waiter/src/poller.ts Outdated Show resolved Hide resolved
packages/util-waiter/src/poller.ts Outdated Show resolved Hide resolved
packages/util-waiter/package.json Outdated Show resolved Hide resolved
packages/util-waiter/package.json Outdated Show resolved Hide resolved
packages/util-waiter/package.json Outdated Show resolved Hide resolved
packages/util-waiter/src/poller.ts Outdated Show resolved Hide resolved
packages/util-waiter/src/poller.ts Outdated Show resolved Hide resolved
packages/util-waiter/src/utils/sleep.spec.ts Outdated Show resolved Hide resolved
packages/util-waiter/src/utils/sleep.spec.ts Outdated Show resolved Hide resolved
packages/util-waiter/src/utils/sleep.spec.ts Outdated Show resolved Hide resolved
packages/util-waiter/src/utils/sleep.ts Outdated Show resolved Hide resolved
packages/util-waiter/src/utils/sleep.spec.ts Outdated Show resolved Hide resolved
@alexforsyth alexforsyth merged commit 02ad0a4 into aws:master Dec 8, 2020

export const waiterTimeout = async (seconds: number): Promise<WaiterResult> => {
await sleep(seconds);
return { state: WaiterState.RETRY };
Copy link
Member

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
Copy link
Member

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))));
Copy link
Member

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This

Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

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).
Copy link
Member

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.

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 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) {
Copy link
Member

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> => {
Copy link
Member

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.

Copy link
Contributor Author

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?

@alexforsyth
Copy link
Contributor Author

#1759

@github-actions
Copy link

github-actions bot commented Jan 8, 2021

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.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jan 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants