Sceptre: AWS CFN - Exceeded request limit

Created on 8 Jul 2019  路  8Comments  路  Source: Sceptre/sceptre

We have a usecase where our CI/CD pipeline updates around 50 cloudformation stacks during the deployment. Because of that we face the request limit exceeded error. Could this be fixed or are there any workarounds?

$ sceptre --var-file config/UAT/MA/mac --var "imageversion=${IMAGE_TAG}" launch  TEST/ecs-service -y
[2019-07-08 09:50:11] - TEST/ecs-service - Launching Stack
[2019-07-08 09:50:25] - Request limit exceeded, pausing...
[2019-07-08 09:50:26] - TEST/ecs-service - Stack is in the UPDATE_COMPLETE state
[2019-07-08 09:50:26] - TEST/ecs-service - Updating Stack
[2019-07-08 09:50:31] - Request limit exceeded, pausing...
[2019-07-08 09:50:42] - Request limit exceeded, pausing...
[2019-07-08 09:50:53] - Request limit exceeded, pausing...
[2019-07-08 09:51:03] - Request limit exceeded, pausing...
[2019-07-08 09:51:10] - Request limit exceeded, pausing...
[2019-07-08 09:51:22] - Request limit exceeded, pausing...
[2019-07-08 09:51:34] - Request limit exceeded, pausing...
[2019-07-08 09:51:53] - Request limit exceeded, pausing...
[2019-07-08 09:52:05] - Request limit exceeded, pausing...
[2019-07-08 09:52:26] - Request limit exceeded, pausing...
[2019-07-08 09:52:44] - Request limit exceeded, pausing...
[2019-07-08 09:53:05] - Request limit exceeded, pausing...
[2019-07-08 09:53:29] - Request limit exceeded, pausing...
[2019-07-08 09:53:49] - Request limit exceeded, pausing...
[2019-07-08 09:54:09] - Request limit exceeded, pausing...
[2019-07-08 09:54:35] - Request limit exceeded, pausing...
[2019-07-08 09:54:59] - Request limit exceeded, pausing...
[2019-07-08 09:55:22] - Request limit exceeded, pausing...
[2019-07-08 09:55:50] - Request limit exceeded, pausing...
[2019-07-08 09:56:17] - Request limit exceeded, pausing...
[2019-07-08 09:56:48] - Request limit exceeded, pausing...
[2019-07-08 09:57:16] - Request limit exceeded, pausing...
[2019-07-08 09:57:49] - Request limit exceeded, pausing...
[2019-07-08 09:58:22] - Request limit exceeded, pausing...
[2019-07-08 09:58:56] - Request limit exceeded, pausing...
[2019-07-08 09:59:33] - Request limit exceeded, pausing...
[2019-07-08 10:00:02] - Request limit exceeded, pausing...
[2019-07-08 10:00:42] - Request limit exceeded, pausing...
[2019-07-08 10:01:15] - Request limit exceeded, pausing...
"Exceeded request limit 30 times. Aborting."
help wanted question

Most helpful comment

We are launching 30+ stacks at once, and we leverage "Decorrelated Jitter" in almost all our boto calls and it has made a big impact, to the point where we don't see failed deployments anymore.

Here is the updated retry function we are testing out now:

connection_manager.py

    @functools.wraps(func)
    def decorated(*args, **kwargs):
        max_retries = 30
        attempts = 1
        mdelay = 1
        delay_cap = 45
        while attempts < max_retries:
            try:
                return func(*args, **kwargs)
            except ClientError as e:
                if e.response["Error"]["Code"] == "Throttling":
                    logger.error("Request limit exceeded, pausing {}...".format(mdelay))
                    time.sleep(mdelay)

                    # Using Decorrelated Jitter Algorithm
                    # We are picking number between a cap of 45 seconds and the last
                    # delay multiplied by 2.5, rounding to two decimal places.
                    mdelay = min(delay_cap, round((random.uniform(1, mdelay * 2.5)), 2))

                    attempts += 1
                else:
                    raise
        raise RetryLimitExceededError(
            "Exceeded request limit {0} times. Aborting.".format(
                max_retries
            )
        )

Great article on it and the impact jitter can have:
https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/

All 8 comments

This is a AWS thing. Its very annoying we get it too with our integration tests occasionally. We reduced the number of concurrent builds to try and help. I don't think there is much we can do from our side.

We are launching 30+ stacks at once, and we leverage "Decorrelated Jitter" in almost all our boto calls and it has made a big impact, to the point where we don't see failed deployments anymore.

Here is the updated retry function we are testing out now:

connection_manager.py

    @functools.wraps(func)
    def decorated(*args, **kwargs):
        max_retries = 30
        attempts = 1
        mdelay = 1
        delay_cap = 45
        while attempts < max_retries:
            try:
                return func(*args, **kwargs)
            except ClientError as e:
                if e.response["Error"]["Code"] == "Throttling":
                    logger.error("Request limit exceeded, pausing {}...".format(mdelay))
                    time.sleep(mdelay)

                    # Using Decorrelated Jitter Algorithm
                    # We are picking number between a cap of 45 seconds and the last
                    # delay multiplied by 2.5, rounding to two decimal places.
                    mdelay = min(delay_cap, round((random.uniform(1, mdelay * 2.5)), 2))

                    attempts += 1
                else:
                    raise
        raise RetryLimitExceededError(
            "Exceeded request limit {0} times. Aborting.".format(
                max_retries
            )
        )

Great article on it and the impact jitter can have:
https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/

Not sure if that is the case here, but I have also seen this when you have stacks that use a lot of !stack_output_external resolvers. I fixed it then by creating a custom resolver that only calls the CFN API once to fetch all stack outputs and caches them on disk. Every subsequent call to that resolver will check if a file is already on disk and uses the cached value if it is there.

PR #803 - This should work for that as well. The resolve leverages the following method connection_manager.call to make the API call and that is wrapped with the retry functionality.

@chhibber I can imagine yes, but with the !stack_output_external it is actually calling the same endpoint every time. So if you have 5 of those in your template, it will basically make 5 of the same calls and only filter out the value it needs. Bit of waste in my opinion, also with smart retries.

@leonrodenburg Understand now and generally agree, though I don't know enough of the overall workflow and if there is some need for realtime calls vs cache.

@chhibber No worries, your solution will work regardless. I just thought I'd drop in a note on how I had solved the issue with resolvers making lots of API calls on my side before. Might be inspiration for others if they face a similar issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

m1keil picture m1keil  路  6Comments

ivanilves picture ivanilves  路  4Comments

fabiodouek picture fabiodouek  路  5Comments

tahoward picture tahoward  路  4Comments

cyphus picture cyphus  路  4Comments