Chalice: [proposal] Rest API with multiple lambda functions

Created on 22 Nov 2019  ยท  5Comments  ยท  Source: aws/chalice

Overview

The current state of the framework deploy the entire project into a single lambda function instance. But for larger projects this can be a problem for several reasons:

  • Monitor individual lambda routes integration (number of invocations, errors, etc).
  • Controlling individually the lambda functions (permissions, throttling, etc).
  • For optimize the lambda function deployment package (.zip).
  • If there is an error with one function, the other ones will not be affected.

Register Handler class

The routes will be registered from each handler that will be implemented separately. Below is an example of the hello handler:

Example of usage:
app.py

from chalice import Chalice


app = Chalice(app_name="helloworld")


app.registrate_route(
    name='hello_world',
    path='/helloworld',
    handler_class=HelloHandler,
)

hello_world.py

from chalice import LambdaHandler


class HelloWorldHandler(LambdaHandler):

    def get(self):
        return {"hello": "world"}

To make packaging possible, the project directory must be structured in a specific way.
as follows:

<project-dir>
โ”œโ”€โ”€ helloworld
โ”‚  โ”œโ”€โ”€ functionlib    # function specific modules
โ”‚  โ”‚  โ”œโ”€โ”€ __init__.py
โ”‚  โ”‚  โ””โ”€โ”€ utils.py
โ”‚  โ”œโ”€โ”€ __init__.py
โ”‚  โ””โ”€โ”€ helloworld.py
โ”œโ”€โ”€ chalicelib    # project shared modules
โ”‚  โ”œโ”€โ”€ __init__.py
โ”‚  โ””โ”€โ”€ generics.py
โ”œโ”€โ”€ app.py
โ”œโ”€โ”€ requirements.txt
โ””โ”€โ”€ etc ...
proposals

Most helpful comment

Hey @jamesls, having one lambda function per route might take us from not having enough lambdas to having too many of them.

It's ok to have multiple methods in a single lambda function and it actually makes sense.
But it would be nice to have the ability to organize your api using different lambdas.

Maybe we can use the resource approach, but make it a new input for the route decorator:

```py
@app.route("/", methods=["GET"], resource="example_resource_1")
def get_1():
return {"hello_world": "this is a get"}

@app.route("/", methods=["POST"], resource="example_resource_1")
def post_1():
return {"hello_world": "this is a post"}

@app.route("/", methods=["GET"], resource="example_resource_2")
def get_2():
return {"hello_world": "this is a get"}

@app.route("/", methods=["POST"], resource="example_resource_2")
def post_2():
return {"hello_world": "this is a post"}
````

Because we have example_resource_1 and example_resource_2 we would end up with 2 lambda functions.

All 5 comments

@jamesls thoughts?

Interesting. We've been using Blueprints to segment our routes. Perhaps that could be leveraged to generate the separate Lambda functions?

As far as the general idea of breaking out your rest API into multiple lambda functions, I am in favor of supporting this. I think it makes a lot of sense. There's a few guidelines I'd like to place on proposals being considered:

  • I'd prefer not to have another way to define routes based on how you're splitting up your lambda functions. I feel that this is largely a matter of implementation/deployment and shouldn't necessarily show up as a separate API in code.
  • To stay backwards compatible, the default behavior doesn't change, this would have to be an opt in feature in the 1.x versions.
  • I'd like to separate the concept of "multiple lambda functions backing your rest API" from "how to more efficiently package your app with multiple lambda functions." I think both are important but can be tackled separately.

Just to throw another idea out there, what if there was no specific API for this? What if it was just a config option in .chalice/config.json (e.g "single_api_lambda": false). We would then create lambda functions with each new @app.route decorator representing a new lambda function. You can already configure which HTTP methods you want routed to it so it gives you the flexibility to group your lambda functions how you want. So for example:

from chalice import Chalice

app = Chalice(app_name="helloworld")

@app.route("/")
def index():
    return {"hello": "world"}

@app.route("/foo")
def foo(): ...

@app.route("/bar", methods=["GET"])
def bar_get(): ...

@app.route("/bar", methods=["POST"])
def bar_post(): ...

The above app would create 4 lambda functions because there's 4 @app.route decorators. It also aligns more closely with how the existing @app.* decorators work, in that each decorator creates a new lambda function.

Implementation-wise this is going to be pretty challenging (a lot of stuff assumes APIHandler), but I'd rather the complication be in the implementation rather than in the user-facing API.

Thoughts?

cc @stealthycoin

Hey @jamesls, having one lambda function per route might take us from not having enough lambdas to having too many of them.

It's ok to have multiple methods in a single lambda function and it actually makes sense.
But it would be nice to have the ability to organize your api using different lambdas.

Maybe we can use the resource approach, but make it a new input for the route decorator:

```py
@app.route("/", methods=["GET"], resource="example_resource_1")
def get_1():
return {"hello_world": "this is a get"}

@app.route("/", methods=["POST"], resource="example_resource_1")
def post_1():
return {"hello_world": "this is a post"}

@app.route("/", methods=["GET"], resource="example_resource_2")
def get_2():
return {"hello_world": "this is a get"}

@app.route("/", methods=["POST"], resource="example_resource_2")
def post_2():
return {"hello_world": "this is a post"}
````

Because we have example_resource_1 and example_resource_2 we would end up with 2 lambda functions.

This seems like a really good pattern.

For me, I need to separate out my Read and Write Lambdas. At present, my Write endpoint handle thousands of writes per minute with only a few concurrent lambdas (3-8) - nice, but the Read endpoint is often sent complex queries which require long DB read times (inner joins and distincts over millions of records etc). At this point more lambdas are spun up cold for the Write endpoint and there is an unncessary cascading impact on performace. It would be great to have the whole application described in one project as above, as it is 'one' project and one API, but I want two Lambdas.

Should we gather the upvotes from https://github.com/aws/chalice/issues/513 too ?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

AtaruOhto picture AtaruOhto  ยท  3Comments

Erstwild picture Erstwild  ยท  4Comments

rupello picture rupello  ยท  4Comments

adsahay picture adsahay  ยท  4Comments

Miserlou picture Miserlou  ยท  4Comments