remove lambda@edge support; use python functools.lru_cache

This commit is contained in:
Brandon Liu
2022-07-18 12:21:12 +08:00
parent ca90de15e9
commit 2d96d92925
4 changed files with 35 additions and 76 deletions

View File

@@ -1,2 +0,0 @@
BUCKET = "example-bucket-name"
REGION = "us-east-1"

View File

@@ -1,20 +1,7 @@
import argparse
import os
import zipfile import zipfile
import sys
parser = argparse.ArgumentParser(
description="Create a deployment-ready PMTiles Lambda zip."
)
parser.add_argument("region", help="AWS Region of the S3 bucket.")
parser.add_argument("bucket", help="S3 Bucket Name.")
args = parser.parse_args()
with zipfile.ZipFile("lambda_function.zip", "w", zipfile.ZIP_DEFLATED) as z: with zipfile.ZipFile("lambda_function.zip", "w", zipfile.ZIP_DEFLATED) as z:
z.write("lambda_function.py") z.write("lambda_function.py")
z.write("../../python/pmtiles/reader.py", "pmtiles.py") z.write("../../python/pmtiles/reader.py", "pmtiles.py")
info = zipfile.ZipInfo("config.py")
info.external_attr = 0o777 << 16
z.writestr(info, f'REGION="{args.region}"\nBUCKET="{args.bucket}"')
print(f"created lambda_function.zip with REGION {args.region} and BUCKET {args.bucket}") print(f"created lambda_function.zip")

View File

@@ -1,16 +1,30 @@
import json
import pmtiles
import re
import boto3
import base64 import base64
import collections import collections
from config import BUCKET, REGION from functools import lru_cache
import gzip
import json
import os
import re
import boto3
import pmtiles
Zxy = collections.namedtuple("Zxy", ["z", "x", "y"]) Zxy = collections.namedtuple("Zxy", ["z", "x", "y"])
s3 = boto3.client("s3") s3 = boto3.client("s3")
rootCache = {}
@lru_cache
def get_object_bytes(key, offset, length):
end = offset + length - 1
return (
s3.get_object(
Bucket=os.environ["BUCKET"],
Key=key,
Range=f"bytes={offset}-{end}",
)
.get("Body")
.read()
)
def parse_tile_uri(str): def parse_tile_uri(str):
@@ -20,60 +34,28 @@ def parse_tile_uri(str):
return (m.group(1), Zxy(int(m.group(2)), int(m.group(3)), int(m.group(4)))) return (m.group(1), Zxy(int(m.group(2)), int(m.group(3)), int(m.group(4))))
def cloudfrontResponse(status_code, body, body_b64=False, headers={}):
headers = {key: [{"value": value}] for key, value in headers.items()}
resp = {"status": status_code, "body": body, "headers": headers}
if body_b64:
resp["bodyEncoding"] = "base64"
return resp
def apiGatewayResponse(status_code, body, body_b64=False, headers={}):
resp = {"statusCode": status_code, "body": body, "headers": headers}
if body_b64:
resp["isBase64Encoded"] = True
return resp
def lambda_handler(event, context): def lambda_handler(event, context):
if "Records" in event: start = datetime.now()
uri = event["Records"][0]["cf"]["request"]["uri"] # CloudFront Origin Request
lambdaResponse = cloudfrontResponse
else:
uri = event["rawPath"] # API Gateway and Lambda Function URLs uri = event["rawPath"] # API Gateway and Lambda Function URLs
lambdaResponse = apiGatewayResponse
tileset, tile = parse_tile_uri(uri) tileset, tile = parse_tile_uri(uri)
if not tile: if not tile:
return lambdaResponse(400, "Invalid tile URL") return {"statusCode": 400, "body": "Invalid Tile URL"}
def get_bytes(offset, length): def get_bytes(offset, length):
global rootCache return get_object_bytes(tileset + ".pmtiles", offset, length)
if offset == 0 and length == 512000 and tileset in rootCache:
return rootCache[tileset]
end = offset + length - 1
result = (
s3.get_object(
Bucket=BUCKET,
Key=tileset + ".pmtiles",
Range=f"bytes={offset}-{end}",
)
.get("Body")
.read()
)
if offset == 0 and length == 512000:
rootCache[tileset] = result
return result
reader = pmtiles.Reader(get_bytes) reader = pmtiles.Reader(get_bytes)
tile_data = reader.get(tile.z, tile.x, tile.y) tile_data = reader.get(tile.z, tile.x, tile.y)
if not tile_data: if not tile_data:
return lambdaResponse(404, "Tile not found") return {"statusCode": 404, "body": "Tile not found"}
headers = { if reader.header().metadata.get("compression") == "gzip":
"Content-Encoding": "gzip", tile_data = gzip.decompress(tile_data)
"Content-Type": "application/protobuf",
"Access-Control-Allow-Origin": "*", return {
"statusCode": 200,
"body": base64.b64encode(tile_data),
"isBase64Encoded": True,
"headers": {"Content-Type": "application/protobuf"},
} }
return lambdaResponse(200, base64.b64encode(tile_data), True, headers)

View File

@@ -1,5 +1,5 @@
import unittest import unittest
from lambda_function import parse_tile_uri, cloudfrontResponse, apiGatewayResponse from lambda_function import parse_tile_uri
class TestLambda(unittest.TestCase): class TestLambda(unittest.TestCase):
@@ -13,14 +13,6 @@ class TestLambda(unittest.TestCase):
tileset, tile = parse_tile_uri("abcd") tileset, tile = parse_tile_uri("abcd")
self.assertEqual(tile, None) self.assertEqual(tile, None)
def test_cloudfront_response(self):
resp = cloudfrontResponse(200, "ok", False, {"a": "b"})
self.assertEqual(resp["headers"]["a"], [{"value": "b"}])
def test_api_gateway_response(self):
resp = apiGatewayResponse(200, "ok", False, {"a": "b"})
self.assertEqual(resp["headers"]["a"], "b")
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()