Skip to content

Commit 7164d7a

Browse files
authored
Merge pull request #946 from laspsandoval/quick_latency_fix
2 parents 2935bfa + 4ee9f6b commit 7164d7a

5 files changed

Lines changed: 130 additions & 74 deletions

File tree

sds_data_manager/constructs/ialirt_api_manager_construct.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,14 @@ def __init__(
223223
handler="IAlirtCode.ialirt_db_query_api.lambda_handler",
224224
runtime=lambda_.Runtime.PYTHON_3_12,
225225
timeout=cdk.Duration.minutes(1),
226-
memory_size=1000,
226+
# Lambda allocates CPU proportionally to memory,
227+
# and DynamoDB queries are often CPU-bound due to
228+
# JSON parsing and network serialization.
229+
memory_size=2048, # MB
227230
environment={
228231
"ALGORITHM_TABLE": algorithm_table.table_name,
229232
"REGION": env.region,
230233
},
231-
layers=layers,
232234
)
233235

234236
# Grant the lambda function read/write permissions on the DynamoDB table.
@@ -250,12 +252,14 @@ def __init__(
250252
handler="IAlirtCode.ialirt_db_query_api_formatted.lambda_handler",
251253
runtime=lambda_.Runtime.PYTHON_3_12,
252254
timeout=cdk.Duration.minutes(1),
253-
memory_size=1000,
255+
# Lambda allocates CPU proportionally to memory,
256+
# and DynamoDB queries are often CPU-bound due to
257+
# JSON parsing and network serialization.
258+
memory_size=2048,
254259
environment={
255260
"ALGORITHM_TABLE": algorithm_table.table_name,
256261
"REGION": env.region,
257262
},
258-
layers=layers,
259263
)
260264

261265
# Grant the lambda function read/write permissions on the DynamoDB table.

sds_data_manager/lambda_code/IAlirtCode/ialirt_db_query_api.py

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44
import logging
55
import os
6+
import time
67
from decimal import Decimal
78

89
import boto3
@@ -11,6 +12,11 @@
1112
logger = logging.getLogger(__name__)
1213
logger.setLevel(logging.INFO)
1314

15+
table_name = os.environ.get("ALGORITHM_TABLE")
16+
region = os.environ.get("AWS_DEFAULT_REGION", "us-west-2")
17+
dynamodb = boto3.resource("dynamodb", region_name=region)
18+
table = dynamodb.Table(table_name)
19+
1420

1521
def process_item_types(item: dict) -> dict:
1622
"""Convert Decimal values to int/float for known fields.
@@ -67,12 +73,11 @@ def lambda_handler(event, context): # noqa: PLR0912
6773
and runtime environment.
6874
6975
"""
70-
table_name = os.environ.get("ALGORITHM_TABLE")
71-
region = os.environ.get("AWS_DEFAULT_REGION", "us-west-2")
72-
dynamodb = boto3.resource("dynamodb", region_name=region)
73-
table = dynamodb.Table(table_name)
76+
t1 = time.perf_counter()
7477

7578
logger.info(f"Received event: {json.dumps(event)}")
79+
80+
# --- Parse event ---
7681
params = event.get("queryStringParameters", {})
7782

7883
if not params:
@@ -83,7 +88,9 @@ def lambda_handler(event, context): # noqa: PLR0912
8388

8489
key_expr = Key("apid").eq(478)
8590
query_kwargs = {"KeyConditionExpression": key_expr}
91+
t2 = time.perf_counter()
8692

93+
# --- Determine key condition ---
8794
allowed_params = {
8895
"met_start",
8996
"met_end",
@@ -183,10 +190,29 @@ def lambda_handler(event, context): # noqa: PLR0912
183190
}
184191

185192
query_kwargs["KeyConditionExpression"] = key_expr
193+
t3 = time.perf_counter()
186194

195+
# --- Query DynamoDB ---
187196
response = table.query(**query_kwargs)
197+
t4 = time.perf_counter()
188198

199+
# --- Process items ---
189200
items = response.get("Items", [])
190201
processed_items = [process_item_types(item) for item in items]
202+
t5 = time.perf_counter()
203+
204+
# --- Serialize to JSON ---
205+
json_body = json.dumps(processed_items)
206+
t6 = time.perf_counter()
207+
208+
num_items = len(processed_items)
209+
210+
text = (
211+
f"Param parse: {t2 - t1:.3f}s | KeyCondition setup: {t3 - t2:.3f}s | "
212+
f"Query: {t4 - t3:.3f}s | Process: {t5 - t4:.3f}s | "
213+
f"JSON: {t6 - t5:.3f}s | TOTAL: {t6 - t1:.3f}s | "
214+
f"Items: {num_items}"
215+
)
216+
logger.info(text)
191217

192-
return {"statusCode": 200, "body": json.dumps(processed_items)}
218+
return {"statusCode": 200, "body": json_body}

sds_data_manager/lambda_code/IAlirtCode/ialirt_db_query_api_formatted.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
logger = logging.getLogger(__name__)
1313
logger.setLevel(logging.INFO)
1414

15+
table_name = os.environ.get("ALGORITHM_TABLE")
16+
region = os.environ.get("AWS_DEFAULT_REGION", "us-west-2")
17+
dynamodb = boto3.resource("dynamodb", region_name=region)
18+
table = dynamodb.Table(table_name)
19+
1520

1621
def process_item_types(item: dict) -> dict:
1722
"""Convert Decimal values to int/float for known fields.
@@ -75,11 +80,6 @@ def lambda_handler(event, context): # noqa: PLR0912, PLR0915
7580
[-2.058, 3.792, -3.989]],
7681
'time_tag_utc': ['2025-10-02T07:07:13', '2025-10-02T07:07:17'], ...}
7782
"""
78-
table_name = os.environ.get("ALGORITHM_TABLE")
79-
region = os.environ.get("AWS_DEFAULT_REGION", "us-west-2")
80-
dynamodb = boto3.resource("dynamodb", region_name=region)
81-
table = dynamodb.Table(table_name)
82-
8383
logger.info(f"Received event: {json.dumps(event)}")
8484
params = event.get("queryStringParameters", {})
8585

tests/lambda_endpoints/test_ialirt_db_query_api.py

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
"""Tests for the I-ALiRT DB Query API Lambda function."""
2+
# ruff: noqa: PLC0415
23

4+
import importlib
35
import json
6+
import os
47
from decimal import Decimal
58

69
import pytest
710

8-
from sds_data_manager.lambda_code.IAlirtCode import ialirt_db_query_api
11+
12+
@pytest.fixture
13+
def ialirt_db_query_api_module(setup_dynamodb):
14+
"""Mock the import."""
15+
os.environ["ALGORITHM_TABLE"] = setup_dynamodb["algorithm_table"].name
16+
os.environ["AWS_DEFAULT_REGION"] = "us-west-2"
17+
18+
from sds_data_manager.lambda_code.IAlirtCode import ialirt_db_query_api
19+
20+
importlib.reload(ialirt_db_query_api)
21+
return ialirt_db_query_api
922

1023

1124
@pytest.fixture
@@ -50,7 +63,7 @@ def algorithm_table(setup_dynamodb):
5063
return table
5164

5265

53-
def test_query_with_met_range(algorithm_table):
66+
def test_query_with_met_range(algorithm_table, ialirt_db_query_api_module):
5467
"""Test query with met range."""
5568
# GET <invoke url>/query?met_start=100&met_end=111
5669
event = {
@@ -59,7 +72,7 @@ def test_query_with_met_range(algorithm_table):
5972
"met_end": "111",
6073
}
6174
}
62-
response = ialirt_db_query_api.lambda_handler(event, context=None)
75+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
6376
items = json.loads(response["body"])
6477
met = sorted(item["met"] for item in items)
6578

@@ -68,38 +81,38 @@ def test_query_with_met_range(algorithm_table):
6881
assert met == expected_data
6982

7083

71-
def test_query_with_met_start(algorithm_table):
84+
def test_query_with_met_start(algorithm_table, ialirt_db_query_api_module):
7285
"""Test query with met start."""
7386
# GET <invoke url>/query?met_start=120
7487
event = {
7588
"queryStringParameters": {
7689
"met_start": "120",
7790
}
7891
}
79-
response = ialirt_db_query_api.lambda_handler(event, context=None)
92+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
8093
items = json.loads(response["body"])
8194
met = sorted(item["met"] for item in items)
8295

8396
expected_data = [120, 130]
8497
assert met == expected_data
8598

8699

87-
def test_query_with_met_end(algorithm_table):
100+
def test_query_with_met_end(algorithm_table, ialirt_db_query_api_module):
88101
"""Test query with met end."""
89102
# GET <invoke url>/query?met_end=120
90103
event = {
91104
"queryStringParameters": {
92105
"met_end": "120",
93106
}
94107
}
95-
response = ialirt_db_query_api.lambda_handler(event, context=None)
108+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
96109

97110
assert response["statusCode"] == 400
98111
expected_message = {"message": "Cannot query by end time without start time"}
99112
assert json.loads(response["body"]) == expected_message
100113

101114

102-
def test_query_with_utc_range(algorithm_table):
115+
def test_query_with_utc_range(algorithm_table, ialirt_db_query_api_module):
103116
"""Test query_with_utc_range."""
104117
# GET <invoke url>/query?met_in_utc_start=<met_in_utc_start>&
105118
# met_in_utc_end=<met_in_utc_end>
@@ -109,7 +122,7 @@ def test_query_with_utc_range(algorithm_table):
109122
"met_in_utc_end": "2021-01-03T00:00:00",
110123
}
111124
}
112-
response = ialirt_db_query_api.lambda_handler(event, context=None)
125+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
113126
items = json.loads(response["body"])
114127

115128
utc = sorted(item["met_in_utc"] for item in items)
@@ -123,15 +136,15 @@ def test_query_with_utc_range(algorithm_table):
123136
assert utc == expected_utc
124137

125138

126-
def test_query_with_utc_start(algorithm_table):
139+
def test_query_with_utc_start(algorithm_table, ialirt_db_query_api_module):
127140
"""Test with insert time start."""
128141
# GET <invoke url>/query?utc_start=<utc_start>
129142
event = {
130143
"queryStringParameters": {
131144
"met_in_utc_start": "2021-01-02T00:00:00",
132145
}
133146
}
134-
response = ialirt_db_query_api.lambda_handler(event, context=None)
147+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
135148
items = json.loads(response["body"])
136149

137150
utcs = sorted(item["met_in_utc"] for item in items)
@@ -145,21 +158,21 @@ def test_query_with_utc_start(algorithm_table):
145158
assert utcs == expected_data
146159

147160

148-
def test_query_with_utc_end(algorithm_table):
161+
def test_query_with_utc_end(algorithm_table, ialirt_db_query_api_module):
149162
"""Test query with insert time end."""
150163
# GET <invoke url>/query?met_in_utc_end=<met_in_utc_end>
151164
event = {
152165
"queryStringParameters": {
153166
"met_in_utc_end": "2021-01-02T00:00:00",
154167
}
155168
}
156-
response = ialirt_db_query_api.lambda_handler(event, context=None)
169+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
157170
assert response["statusCode"] == 400
158171
expected_message = {"message": "Cannot query by end time without start time"}
159172
assert json.loads(response["body"]) == expected_message
160173

161174

162-
def test_query_no_results(algorithm_table):
175+
def test_query_no_results(algorithm_table, ialirt_db_query_api_module):
163176
"""Test query if there are no results."""
164177
# GET <invoke url>/query?met_start=<met_start>&met_end=<met_end>
165178
event = {
@@ -168,12 +181,12 @@ def test_query_no_results(algorithm_table):
168181
"met_end": "300",
169182
}
170183
}
171-
response = ialirt_db_query_api.lambda_handler(event, context=None)
184+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
172185
assert response["statusCode"] == 200
173186
assert json.loads(response["body"]) == []
174187

175188

176-
def test_query_with_multiple_filters(algorithm_table):
189+
def test_query_with_multiple_filters(algorithm_table, ialirt_db_query_api_module):
177190
"""Test query with multiple filters."""
178191
# GET <invoke url>/query?met_start=100&met_end=130&product_name=codicelo_product_1
179192
event = {
@@ -182,13 +195,13 @@ def test_query_with_multiple_filters(algorithm_table):
182195
"met_end": "130",
183196
}
184197
}
185-
response = ialirt_db_query_api.lambda_handler(event, context=None)
198+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
186199

187200
items = json.loads(response["body"])
188201
assert len(items) == 4
189202

190203

191-
def test_query_with_different_time_queries(algorithm_table):
204+
def test_query_with_different_time_queries(algorithm_table, ialirt_db_query_api_module):
192205
"""Test query API with multiple filters."""
193206
# GET <invoke url>/query?met_start=100&met_end=130&product_name=hit*&
194207
# met_in_utc_start=2021-01-02T00:00:00.
@@ -199,41 +212,41 @@ def test_query_with_different_time_queries(algorithm_table):
199212
"met_in_utc_start": "2021-01-02T00:00:00",
200213
}
201214
}
202-
response = ialirt_db_query_api.lambda_handler(event, context=None)
215+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
203216
assert response["statusCode"] == 400
204217
expected_message = {
205218
"message": "Cannot query multiple time keys (met, met_in_utc, last_modified)"
206219
}
207220
assert json.loads(response["body"]) == expected_message
208221

209222

210-
def test_query_with_invalid_parameters(algorithm_table):
223+
def test_query_with_invalid_parameters(algorithm_table, ialirt_db_query_api_module):
211224
"""Test query with invalid parameters."""
212225
# GET <invoke url>/query?met_bad=100.
213226
event = {
214227
"queryStringParameters": {
215228
"met_bad": "100",
216229
}
217230
}
218-
response = ialirt_db_query_api.lambda_handler(event, context=None)
231+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
219232

220233
assert response["statusCode"] == 400
221234
expected_message = {"message": "Unexpected parameters: met_bad"}
222235
assert json.loads(response["body"]) == expected_message
223236

224237

225-
def test_query_with_no_parameters(algorithm_table):
238+
def test_query_with_no_parameters(algorithm_table, ialirt_db_query_api_module):
226239
"""Test query with no parameters."""
227240
# GET <invoke url>/query.
228241
event = {"queryStringParameters": None}
229-
response = ialirt_db_query_api.lambda_handler(event, context=None)
242+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
230243

231244
assert response["statusCode"] == 400
232245
expected_message = {"message": "No query parameters provided"}
233246
assert json.loads(response["body"]) == expected_message
234247

235248

236-
def test_query_with_mixed_parameters(algorithm_table):
249+
def test_query_with_mixed_parameters(algorithm_table, ialirt_db_query_api_module):
237250
"""Test query with mixed parameters."""
238251
# GET <invoke url>/query?met_start=100&met_in_utc_end=2021-01-02T00:00:00.
239252
event = {
@@ -242,7 +255,7 @@ def test_query_with_mixed_parameters(algorithm_table):
242255
"met_in_utc_end": "2021-01-02T00:00:00",
243256
}
244257
}
245-
response = ialirt_db_query_api.lambda_handler(event, context=None)
258+
response = ialirt_db_query_api_module.lambda_handler(event, context=None)
246259

247260
assert response["statusCode"] == 400
248261
expected_message = {
@@ -251,7 +264,7 @@ def test_query_with_mixed_parameters(algorithm_table):
251264
assert json.loads(response["body"]) == expected_message
252265

253266

254-
def test_process_item_types():
267+
def test_process_item_types(ialirt_db_query_api_module):
255268
"""Test process_item_types function."""
256269
items = [
257270
{
@@ -265,7 +278,9 @@ def test_process_item_types():
265278
}
266279
]
267280

268-
processed_items = [ialirt_db_query_api.process_item_types(item) for item in items]
281+
processed_items = [
282+
ialirt_db_query_api_module.process_item_types(item) for item in items
283+
]
269284

270285
assert processed_items == [
271286
{

0 commit comments

Comments
 (0)