Get Your Lambda...

Damian Wysocki

Django

Django

Lambda

HTTP REQUEST

Lambda

  • Debugging Lambda locally is painful

The  Developer’s Dilemma

The  Developer’s Dilemma

Slow feedback.

       Hard to debug.

              Not Pythonic.

Traditional Lambda Workflow

Meet the Hero!

  • Simulates AWS Lambda locally
  • Cold/warm start support
  • Custom events & context
  • Easy setup
  • Customizability
  • Auto-reloading
  • State Persistence
[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.saleor_handler]
handler_path = "handler.http_handler"
url_path = "/saleor/{path:path}"
def http_handler(event, context):
    print("Hello from Lambda")
    return {
        "statusCode": 200,
        "body": "Hello World"
    }
python -m smyth
def http_handler(event, context):
    print("Hello from Lambda")
    return {
        "statusCode": 200,
        "body": "Hello World"
    }

Add ipdb.set_trace() for real debugging!

def http_handler(event, context):
    import ipdb;ipdb.set_trace()
    
    return {
        "statusCode": 200,
        "body": "Hello World"
    }

HTTP

REQUEST

Lambda

EVENT

HTTP

REQUEST

Lambda

EVENT

API GW V2

HTTP

REQUEST

EVENT

Lambda

API GW V1

HTTP

REQUEST

EVENT

Lambda

def http_handler(event, context):
    print("Hello from Lambda")
    return {
        "statusCode": 200,
        "body": "Hello World"
    }
from smyth.types import EventData, RunnerProcessProtocol, SmythHandler


async def generate_api_gw_v1_event_data(
	request: Request,
    smyth_handler: SmythHandler,
    process: RunnerProcessProtocol
) -> EventData:
	return {
    	... # API GW V1 event
    }



def http_handler(event, context):
    print("Hello from Lambda")
    return {
        "statusCode": 200,
        "body": "Hello World"
    }
[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.saleor_handler]
handler_path = "handler.http_handler"
url_path = "/saleor/{path:path}"
[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.saleor_handler]
handler_path = "handler.http_handler"
url_path = "/saleor/{path:path}"
event_data_function_path = "handler.generate_api_gw_v1_event_data"

more complex example

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.saleor_handler]
handler_path = "handler.http_handler"
url_path = "/saleor/{path:path}"
event_data_function_path = "handler.generate_api_gw_v1_event_data"
def order_handler(event, context):
    lambda_client.invoke(
        FunctionName="email_handler",  
        InvocationType="Event",  # or RequestResponse
        Payload=b'{"to": "hello@mirumee.com", "subject": "Order made"}',
    )
    return {"statusCode": 200, "body": f"Orders requests: {COUNT}"}

def email_handler(event, context):
    print(event)  
    return {"statusCode": 200, "body": f"Products requests: {COUNT}"}

more complex example

def order_handler(event, context):
    lambda_client.invoke(
        FunctionName="email_handler",  
        InvocationType="Event",  # or RequestResponse
        Payload=b'{"to": "hello@mirumee.com", "subject": "Order made"}',
    )
    return {"statusCode": 200, "body": f"Orders requests: {COUNT}"}

def email_handler(event, context):
    print(event)  
    return {"statusCode": 200, "body": f"Products requests: {COUNT}"}

more complex example

import boto3

lambda_client = boto3.client(
    "lambda", 
    endpoint_url="http://localhost:8080"  
)  
def order_handler(event, context):
    lambda_client.invoke(
        FunctionName="email_handler",  
        InvocationType="Event",  # or RequestResponse
        Payload=b'{"to": "hello@mirumee.com", "subject": "Order made"}',
    )
    return {"statusCode": 200, "body": f"Orders requests: {COUNT}"}

more complex example

import boto3

lambda_client = boto3.client(
    "lambda", 
    endpoint_url="http://localhost:8080"  
)  
def order_handler(event, context):
    lambda_client.invoke(
        FunctionName="email_handler",  
        InvocationType="Event",  # or RequestResponse
        Payload=b'{"to": "hello@mirumee.com", "subject": "Order made"}',
    )
    return {"statusCode": 200, "body": f"Orders requests: {COUNT}"}

more complex example

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.saleor_handler]
handler_path = "handler.http_handler"
url_path = "/saleor/{path:path}"

more complex example

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"

[tool.smyth.handler.email_handler]
handler_path = "email_handler"
url_path = "/emails/{path:path}"
[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"

[tool.smyth.handler.email_handler]
handler_path = "handler.email_handler"
url_path = "/emails/{path:path}"
import boto3

lambda_client = boto3.client(
    "lambda", 
    endpoint_url="http://localhost:8080"  
)  
def order_handler(event, context):
    lambda_client.invoke(
        FunctionName="email_handler",  
        InvocationType="Event",  # or RequestResponse
        Payload=b'{"to": "hello@mirumee.com", "subject": "Order made"}',
    )
    return {"statusCode": 200, "body": f"Orders requests: {COUNT}"}
  • Each handler runs in a separate process
  • Cold start: New process, code reloaded
  • Warm start: Process reused, state preserved
  • Simulate real AWS Lambda concurrency

Concurrency: Cold & Warm Start

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"

[tool.smyth.handler.email_handler]
handler_path = "handler.email_handler"
url_path = "/emails/{path:path}"
[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"

[tool.smyth.handler.email_handler]
handler_path = "handler.email_handler"
url_path = "/emails/{path:path}"

order_handler

email_handler

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"
concurrency = 2


[tool.smyth.handler.email_handler]
handler_path = "handler.email_handler"
url_path = "/emails/{path:path}"
concurrency = 2

order_handler

email_handler

order_handler

email_handler

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.first_warm"


[tool.smyth.handler.email_handler]
handler_path = "handler.email_handler"
url_path = "/emails/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.first_warm"

order_handler

email_handler

order_handler

email_handler

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.first_warm"


[tool.smyth.handler.email_handler]
handler_path = "handler.email_handler"
url_path = "/emails/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.first_warm"

order_handler

email_handler

order_handler

email_handler

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.first_warm"


[tool.smyth.handler.email_handler]
handler_path = "handler.email_handler"
url_path = "/emails/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.round_robin"

order_handler

email_handler

order_handler

email_handler

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.first_warm"


[tool.smyth.handler.email_handler]
handler_path = "handler.email_handler"
url_path = "/emails/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.round_robin"

order_handler

email_handler

order_handler

email_handler

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.first_warm"


[tool.smyth.handler.email_handler]
handler_path = "handler.email_handler"
url_path = "/emails/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.round_robin"

order_handler

email_handler

order_handler

email_handler

[tool.smyth]
host = "0.0.0.0"
port = 8080

[tool.smyth.handlers.order_handler]
handler_path = "handler.order_handler"
url_path = "/orders/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.first_warm"


[tool.smyth.handler.email_handler]
handler_path = "handler.email_handler"
url_path = "/emails/{path:path}"
concurrency = 2
strategy_function_path = "smyth.runner.strategy.round_robin"

order_handler

email_handler

order_handler

email_handler

Why lambda?

  • auto-scaling
  • easy deployment
  • it's cheap

Why lambda?

  • auto-scaling
  • easy deployment
  • it's cheap - it is?

dodac link do kalkulatora

Meet the Hero

 

 

 

 

 

 

Lynara: ASGI Apps on Lambda

import asyncio
from lynara import Lynara, APIGatewayProxyEventV2Interface
from fastapi import FastAPI

app = FastAPI()
lynara = Lynara(app=app)

def lambda_handler(event, context):
    return asyncio.run(
        lynara.run(event, context, APIGatewayProxyEventV2Interface)
    )

No rewrite needed. Your ASGI app is portable!

Lambda

HTTP REQUEST

EVENT

ASGI

benchmarks

Serverless Dev Flow, Your Way

  • Lambda is cheap/free for dev & test
  • No infra to manage - perfect for startups, MVPs
  • Outgrow Lambda? Move to EC2/Docker/K8s - no rewrite
  • Scale down to zero your ASGI App - serverless

Ready to Supercharge Your Python Serverless?

Try Smyth for local Lambda dev.
Deploy with Lynara for serverless FastAPI/Django.

Questions?

Smyth GitHub | Lynara GitHub

Made with Slides.com