A website-performance API running on AWS Lambda. It is built with FastAPI and uses DuckDB to query website request logs stored as Parquet files in S3. Given a date range and an optional page, it returns request count, median and p95 response time, error count and error rate.
Loading DuckDB and FastAPI at startup makes cold starts slow, so this project measures what Lambda SnapStart does about it. The same API is deployed twice, once with SnapStart and once without, and the two are compared under identical conditions.
Article.md walks through the implementation and the results.
Both functions deploy the same ZIP at 1024 MB. Only two things differ:
| Handler | SnapStart | |
|---|---|---|
web-perf-without-snapstart |
app.lambda_handler |
off |
web-perf-with-snapstart |
app_snapstart.lambda_handler |
on published versions |
Every command below is bash and assumes Linux.
- Python 3.12 with
venv - Node.js 22 or newer. The CDK CLI is a Node package, and even a Python CDK app spawns
nodethrough jsii, so without itcdk synthfails withFileNotFoundError: [Errno 2] No such file or directory: 'node'. Distro packages are often too old; jsii warns on Node 20 and below, so install from NodeSource or nvm - Docker, running, with your user in the
dockergroup. The bundler builds the Linux DuckDB wheel and bakes in the signedhttpfsandawsextensions, so the functions never download extensions at startup - AWS credentials, the CDK CLI (
npm install -g aws-cdk), and a bootstrapped CDK environment
On Debian or Ubuntu, once Node is installed:
sudo apt install python3.12 python3.12-venv docker.io
sudo usermod -aG docker "$USER" # log out and back in for this to applypython3 -m venv .venv
source .venv/bin/activate
python -m pip install -r cdk/requirements.txt -r tests/requirements.txtKeep the virtualenv active for everything below. cdk.json runs python app.py, so cdk deploy fails with ModuleNotFoundError: No module named 'aws_cdk' if it is not.
cd cdk
cdk bootstrap # once per account and Region
cdk deploy
cd ..The stack name defaults to python-snapstart-comparison-cdk. Override it with cdk deploy -c stackName=my-stack-name.
The SnapStart version stays Pending while Lambda initializes it and builds the snapshot, so this deploy is slower than a normal one.
Generate four Parquet files holding 250,000 synthetic requests, then sync them to the bucket the stack created:
python scripts/generate_sample_data.py \
--output .tmp/website-data \
--rows 250000 \
--files 4
BUCKET=$(aws cloudformation describe-stacks \
--stack-name python-snapstart-comparison-cdk \
--query "Stacks[0].Outputs[?OutputKey=='WebsiteDataBucketName'].OutputValue | [0]" \
--output text)
aws s3 sync .tmp/website-data "s3://$BUCKET/website-requests/"Both API URLs are in the stack outputs and already include the prod stage:
GET .../prod/health
GET .../prod/analytics/website?from_date=2026-08-01&to_date=2026-08-12&page=/search
Dates and the page are bound as SQL parameters. The S3 URI comes from the function's environment, so a caller cannot point DuckDB at another bucket.
python -m pytestThey write temporary local Parquet files and query them with the same class Lambda uses, so no AWS credentials are needed.
src/app.py— FastAPI app, DuckDB connection, S3 auth and the SQL. No SnapStart codesrc/app_snapstart.py— importsapp, registers the after-restore hook, re-exports the handlercdk/— bucket, both functions, versions, aliases, REST APIs, roles, log groups and outputsscripts/generate_sample_data.py— Parquet generatortests/— query, validation, adapter, SnapStart hook and CDK assertions
cd cdk
cdk destroyThe bucket has a RETAIN policy, so empty and delete it yourself when you no longer need the data.
Every published SnapStart version keeps a cached snapshot that is billed for at least three hours, so delete versions you are done with.
