diff --git a/serverless/aws/README.md b/serverless/aws/README.md index f11e701..7f44c5e 100644 --- a/serverless/aws/README.md +++ b/serverless/aws/README.md @@ -5,10 +5,12 @@ Upload the resulting `lambda_function.zip` using the Lambda console. -## Restrictions -1. There is a limit of 1 MB for tiles served through Lambda@Edge. -2. Lambda@Edge does not support layers, environment variables, or ARM functions. +## Configuration + +* `BUCKET`: the S3 bucket name. +* `PMTILES_PATH` +* `TILE_PATH` ## AWS Notes @@ -17,28 +19,19 @@ Upload the resulting `lambda_function.zip` using the Lambda console. ## Test Event -CloudFront event: - -```json -{ - "Records": [ - { - "cf": { - "request": { - "uri": "/my-tileset-name/0/0/0.pbf", - "method": "GET", - "headers": {} - } - } - } - ] -} -``` - API Gateway V2 / Lambda Function URLs: ```json { "rawPath": "/my-tileset-name/0/0/0.pbf" } -``` \ No newline at end of file +``` + + +### Monitoring + + + +### Lambda@Edge + +Lambda@Edge's multi-region features have little benefit when fetching data from S3 in a single region, and Lambda@Edge [doesn't support](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/edge-functions-restrictions.html) environment variables or responses over 1 MB. For globally distributed caching, use CloudFront in combination with Lambda Function URLs. \ No newline at end of file diff --git a/serverless/aws/create_lambda_function.py b/serverless/aws/create_lambda_function.py index f95f89a..b3a07c1 100644 --- a/serverless/aws/create_lambda_function.py +++ b/serverless/aws/create_lambda_function.py @@ -1,7 +1,11 @@ import zipfile +import subprocess + +sha = subprocess.check_output(["git", "describe", "--always"]).strip() with zipfile.ZipFile("lambda_function.zip", "w", zipfile.ZIP_DEFLATED) as z: z.write("lambda_function.py") z.write("../../python/pmtiles/reader.py", "pmtiles/reader.py") + z.writestr("version",sha) print(f"created lambda_function.zip") diff --git a/serverless/aws/lambda_function.py b/serverless/aws/lambda_function.py index 8a644de..df03eb1 100644 --- a/serverless/aws/lambda_function.py +++ b/serverless/aws/lambda_function.py @@ -8,6 +8,7 @@ import re # Exists inside all lambda functions import boto3 +from botocore.exceptions import ClientError # create_lambda_function.py will vendor the relevant file from pmtiles.reader import Reader @@ -16,7 +17,6 @@ Zxy = collections.namedtuple("Zxy", ["z", "x", "y"]) s3 = boto3.client("s3") - # Given a 512MB lambda function, use half of the memory for the cache, # assuming the average root/leaf/tile size is 512 KB @lru_cache(maxsize=500) @@ -64,7 +64,7 @@ def lambda_handler(event, context): name, tile = parse_tile_path(os.environ.get("TILE_PATH"), uri) if not tile: - return {"statusCode": 400, "body": "Invalid Tile URL"} + return {"statusCode": 400, "body": "Invalid tile URL"} def get_bytes(offset, length): return get_object_bytes( @@ -72,10 +72,22 @@ def lambda_handler(event, context): ) reader = Reader(get_bytes) - tile_data = reader.get(tile.z, tile.x, tile.y) - if not tile_data: + minzoom = int(reader.header().metadata["minzoom"]) + maxzoom = int(reader.header().metadata["maxzoom"]) + if tile.z < minzoom or tile.z > maxzoom: return {"statusCode": 404, "body": "Tile not found"} + try: + tile_data = reader.get(tile.z, tile.x, tile.y) + if not tile_data: + return {"statusCode": 204} + except ClientError as e: + error_code = e.response["Error"]["Code"] + if error_code == "AccessDenied": + return {"statusCode": 404, "body": "Archive not found"} + else: + raise e + # CloudFront requires decompressed responses from lambda # in order to implement the Compressed CacheOptimized policy correctly # as well as Brotli support