Skip to content

Commit 85748a0

Browse files
committed
source-zendesk-support-native: reduce memory usage by streaming incremental export resources
Using the new `http.request_object_stream` to stream incremental export resources signficantly reduces the memory usage of the connector, even when using the max page size of 1000.
1 parent 08a4b57 commit 85748a0

File tree

2 files changed

+111
-14
lines changed

2 files changed

+111
-14
lines changed

source-zendesk-support-native/poetry.lock

+104
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

source-zendesk-support-native/source_zendesk_support_native/api.py

+7-14
Original file line numberDiff line numberDiff line change
@@ -313,20 +313,21 @@ async def backfill_satisfaction_ratings(
313313
yield end
314314

315315

316-
async def _make_incremental_cursor_export_request(
316+
async def _fetch_top_level_fields(
317317
http: HTTPSession,
318318
url: str,
319319
params: dict[str, str | int],
320320
response_model: type[IncrementalCursorExportResponse],
321321
log: Logger,
322-
) -> IncrementalCursorExportResponse:
322+
) -> tuple[str | None, bool]:
323323
# Instead of using Pydantic's model_validate_json that uses json.loads internally,
324324
# use json.JSONDecoder().raw_decode to reduce memory overhead when processing the response.
325325
raw_response_bytes = await http.request(log, url, params=params)
326326
obj, _ = json.JSONDecoder().raw_decode(raw_response_bytes.decode('utf-8'))
327327
# model_construct is used to avoid validating & transforming all resources within large response bodies at once
328328
# to reduce memory overhead. Instead, resources are validated & transformed one-by-one as they are yielded.
329-
return response_model.model_construct(**obj)
329+
response = response_model.model_construct(**obj)
330+
return (response.after_cursor, response.end_of_stream)
330331

331332

332333
async def _fetch_incremental_cursor_export_resources(
@@ -360,21 +361,13 @@ async def _fetch_incremental_cursor_export_resources(
360361
params["cursor"] = _base64_encode(cursor)
361362

362363
while True:
363-
response = await _make_incremental_cursor_export_request(http, url, params, response_model, log)
364-
365-
next_page_cursor = response.after_cursor
366-
end_of_stream = response.end_of_stream
367-
368-
next_page_cursor = response.after_cursor
369-
end_of_stream = response.end_of_stream
364+
next_page_cursor, end_of_stream = await _fetch_top_level_fields(http, url, params, response_model, log)
370365

371366
if next_page_cursor is None:
372367
return
373368

374-
for resource in response.resources:
375-
# Since _make_incremental_cursor_export_request does not validate & transform all resources at once,
376-
# we validate them one-by-one as they're yielded.
377-
yield TimestampedResource.model_validate(resource)
369+
async for resource in http.request_object_stream(log, TimestampedResource, f"{name}.item", url, params=params):
370+
yield resource
378371

379372
yield _base64_decode(next_page_cursor)
380373

0 commit comments

Comments
 (0)