10000 WIP: Add high resource count support by pmuens · Pull Request #3441 · serverless/serverless · GitHub
[go: up one dir, main page]

Skip to content

WIP: Add high resource count support #3441

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

Closed
wants to merge 5 commits into from

Conversation

pmuens
Copy link
Contributor
@pmuens pmuens commented Apr 3, 2017

Note: This is the first naive iteration. It's not 100% working right now and a better splitting algorithm (e.g. based on resource groups) is needed! Furthermore it's a WIP. The deployment fails because of unresolved refs etc.

Todos

  • Build dependency graph to resolve Refs etc.
  • Switch from naive stack splitting to resource grouped splitting
  • DRY out code / fix Todo comments
  • Run npm shrinkwrap (currently fails on my machine)
  • Print log message when resource count is nearly reached + add link do docs for stack splitting
  • Translate AWS error message ("Resource count is over 200@") into useful error message + add link do docs for stack splitting
  • Use provider.naming to centralize resource naming (e.g. nested stack resource logical ids)
  • Write tests
  • Write documentation
  • Fix linting errors
  • Make sure code coverage hasn't dropped
  • Provide verification config / commands / resources
  • Update the messages below

What did you implement:

Closes #3411

Add automatic stack splitting when service contains a high number of resources.

How did you implement it:

Serverless will check the compiled CloudFormation template before deploying to AWS. It will split the stack up into nested stacks if the resource count is above X resources.

After that it will upload the nested stack templates to the S3 Bucket and update the compiled CloudFormation template which is in memory. This makes this whole change encapsulated and backwards compatible.

The user has to specifically opt-in for this feature via the useStackSplitting: true config.

How can we verify it:

Create a service which looks like the following:

service: service

provider:
  name: aws
  runtime: nodejs4.3
  useStackSplitting: true

functions:
  hello:
    handler: handler.hello
    events:
      - http:
          method: ANY
          path: hello
          integration: LAMBDA
          cors: true
  goodbye:
    handler: handler.goodbye
    events:
      - http:
          method: GET
          path: goodbye

Run serverless deploy --noDeploy and look into the .serverless directory to see all the resources.

Or run serverless deploy to deploy the stack.


Is this ready for review?: NO
Is it a breaking change?: NO

/cc @brianneisler @eahefnawy @dougmoscrop

@pmuens pmuens added this to the 1.11 milestone Apr 3, 2017
const stackResource = _.cloneDeep(stackResourceTemplate);

const stackNumber = index + 1;
const resourceLogicalId = `NestedStack${stackNumber}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

This should probably use provider.naming

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! Yep 100% agree...

@dougmoscrop
Copy link
Contributor

This is mostly off the top of my head since I was setting out to work on this very same problem (as a plugin), from a design perspective maybe this could start by considering resources of known types, because then you can manage how much knowledge you have to have about the stack; for example CloudWatch::Metric objects are our most frequent resource right now, and so moving them to a separate stack can be done easily by understanding the points in the object structure that reference resources that will be downstream.

.then(this.generateStacks)
.then(this.uploadStackFiles)
.then(this.updateCompiledCloudFormationTemplate)
.then(this.writeStacksToDisk);
Copy link
Contributor

Choose a reason for hiding this comment

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

worth doing this before the upload so that the can be seen for debug?

Copy link
Contributor

Choose a reason for hiding this comment

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

For the new package/deploy semantics, it should be done in the deployment step, not the package step. Otherwise the deploy command would have to have too much knowledge about the stack splitting itself.
This has to be refactored anyway as soon as the new commands are released to hook the new command hooks.
Imo this should be discussed at a later time.

Copy link
Contributor Author
@pmuens pmuens Apr 5, 2017

Choose a reason for hiding this comment

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

Thanks for the feedback @dougmoscrop and @HyperBrain

Right now it 10000 's implemented this way so that we have all the URLs (e.g. to the function zips) in place.

Yes, this definitely needs an update once the new package and deploy support is in place.

@pmuens pmuens modified the milestones: 1.11, 1.12 Apr 6, 2017
@pmuens pmuens force-pushed the high-resource-count-support branch from 0a8161c to cfc1687 Compare April 6, 2017 12:23
@@ -0,0 +1,129 @@
'use strict';
Copy link
Contributor

Choose a reason for hiding this comment

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

Why not some existing npm module?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. We tried existing ones, but both were not able to compute distinct clusters (here's one commit where we tried one --> f8d2935).

This code is able to compute non-overlapping clusters.


const stacks = [];

// TODO update so that no stack splitting is done at all if not enough resources are present?
Copy link
Contributor

Choose a reason for hiding this comment

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

yes please! No need to complicate simple services which most of our users have.

@eahefnawy
Copy link
Contributor

Pretty cool @pmuens ... I think you'll need to make some changes to accommodate the recent changes in packaging/deployment. Best guess, most of the logic would be in the package plugin.

@dougmoscrop
Copy link
Contributor
dougmoscrop commented Apr 18, 2017 via email

@HyperBrain
Copy link
Contributor
HyperBrain commented Apr 18, 2017

@eahefnawy I'm not sure if I agree with the package plugin as the container of the logics. It would be much easier to have it at the deploy plugin (somewhere before the uploadArtifacts event). Then the artifacts transferred from package to deploy would contain the original (non-split) CF template, and the deploy step would split them into the different stacks - imo stack splitting is a deployment feature not a package/build feature.
With that approach you also do not have to persist stack-related information to deploy (there will be additional CF scripts for each nested stack) - and more important: Plugins can decide to work on the complete template or the split template by hooking differently in deploy. The split operation itself could be exposed as additional hook like aws:deploy:splitStack.

@pmuens
Copy link
Contributor Author
pmuens commented Apr 18, 2017

@eahefnawy @HyperBrain thanks for the update on how to resolve the conflicts.

I agree with @dougmoscrop that this might be better off in a separate plugin. The way we've planned and implemented it here won't work since everything is so dependent on each other that it will be super complicated to separate all the resources into separete, nested stacks.

Furthermore there's #3475 which could be used to solve the problems this PRs tries to solve.

@dougmoscrop do you have any idea how this should be implemented as a plugin?

@dougmoscrop
Copy link
Contributor

So I have an idea how I might implement such a plugin, which is I am going to try to move functions to their own individual stacks, and then move known resource types to those stacks (DLQ, CloudWatch items, etc.) and then figure out how to parameterize those sub-stacks (e.g. detect any Ref type values and make them stack parameters).

I'm a bit behind on serverless developments, but the separate package vs. deploy step, that does not include the CF template right? So CF templates are still generated on deploy?

@HyperBrain
Copy link
Contributor
HyperBrain commented Apr 18, 2017

@dougmoscrop The compiled (finalized) CF template is available on deploy time as this.serverless.service.provider.compiledCloudFormationTemplate and is created in the package phase.

Typically the deploy phase would be the target for any split operations done by a plugin. With the new lifecycle approach there are a bunch of new hookable events, that should perfectly fit for the plugin.
You could try to hook before:aws:deploy:deploy:createStack to do all the modifications to the compiled template. This makes sure that other plugins, that rely on one integral CF template during the build phase and early deploy phases continue to work and play nicely with the new plugin.
Additionally the plugin should hook after:aws:deploy:deploy:uploadArtifacts to upload the additionally created CF nested stack templates into the S3 bucket - at that point the bucketname is available - so that the updateStack event which is ran afterwards, will be able to update the stack including the nested stacks.

There should be no further hooks than the 2 needed.

@pmuens
Copy link
Contributor Author
pmuens commented Apr 19, 2017

@dougmoscrop thanks for jumping in and providing a possible solution / proposal for a plugin 👍

@HyperBrain thanks for more clarifications around the new package / deploy separation.

We've discussed this PR yesterday and came to the conclusion that it's way too complex (and nearly impossible) to cluster the whole CloudFormation template with all its dependencies into separate nested stacks.

After having some feedback in #3411 it seems like using Fn::ImportValue in combination with Outputs might be a better way to tackle the problem this PR tries to solve.

Furthermore it looks like users want to have control over this process rather than having Serverless do its thing automagically.

We've started a WIP PR for native imports and exports configurations: #3475

This feature can be used to split stacks into separate units of deployment.

Anyway we'd really love to have a plugin which approaches an automatic stack splitting. So it would be awesome if you could keep us posted on your process about this @dougmoscrop

We'd be happy to promote this plugin as the go-to solution for this specific problem.

I'll close this PR for now since we won't work on it anymore (at least for now).


@dougmoscrop let us know if we can help you with the plugin development!

@pmuens pmuens closed this Apr 19, 2017
@pmuens pmuens mentioned this pull request Apr 21, 2017
8 tasks
@pmuens pmuens deleted the high-resource-count-support branch April 21, 2017 14:36
@pmuens
Copy link
Contributor Author
pmuens commented Apr 21, 2017

Another update.

We've tackled the problem again and are now working on a solution in #3504.

The implementation is based on @dougmoscrop idea to create nested stacks based on functions and their dependants.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Introduce solution for services with a high resource count
5 participants
0