Skip to content

Keep large pipeline runs and service-entry results under the DynamoDB item limit - #54

Closed
zzkamzn wants to merge 1 commit into
aws-samples:mainfrom
zzkamzn:fix/item-size-limits
Closed

zzkamzn wants to merge 1 commit into
aws-samples:mainfrom
zzkamzn:fix/item-size-limits

Conversation

@zzkamzn

@zzkamzn zzkamzn commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Problem

Two record types could grow past DynamoDB's 400 KB item limit, and in both cases the failure surfaced as something else.

Pipeline runs.

  • Every agent() call list_appended an entry, about 300–400 bytes, onto the run item.
  • A run can last 45 minutes with a fan-out of 8, so hundreds or thousands of calls are reachable.
  • The same item also holds up to 200 log lines of 500 characters and a 24,000-character result. With CJK text those alone come to roughly 370 KB.
  • Once the item was full, the append raised and the run failed. A busy workflow was the one that broke.

Service-entry results.

  • result was capped at 200,000 characters, which is up to 600 KB of CJK.
  • The final update_item then failed and the worker recorded the invocation as failed. The caller polling for the answer of a successful call got a failure.

Change

Pipeline runs

  • Each agent call is its own item in the run's partition: PK=PIPELINERUN#<run_id>, SK=AGENT#<ts>#<rand>.
  • The run item gets atomic agents_total and cost_usd_total counters, and keeps the first 100 calls as a preview, enforced by a size(agents) < :cap condition. Run lists therefore cost the same as before and still show calls inline.
  • GET /api/v1/pipeline-runs/{id} returns every call.
  • When a run ends, completed or failed, its per-phase stats are computed over all calls and stored as phases_summary. The Insights view=summary reads that instead of the preview. Running runs and runs recorded before this change fall back to the inline list, exactly as before.
  • The result and logs are capped in UTF-8 bytes: 72 KB and 96 KB.
  • The Pipeline page loads the full call list when a large run's card is opened, and shows the exact cost total.

Service-entry results

  • Capped at 300,000 bytes of UTF-8, with result_truncated returned to the caller. This is documented in docs/user-guide.md.

There are no Terraform or IAM changes; the backend already has PutItem/Query on the table.

Test plan

  • New backend/tests/test_item_size_limits.py, 5 tests:
    • 1,500 calls keep the run item under 100 KB, with a 100-call preview, exact totals and an exact cost.
    • The list shows the preview plus totals while the detail shows every call.
    • phases_summary covers every call.
    • The result and logs are capped in bytes.
    • A 900 KB CJK service-entry result is cut to 300 KB, flagged, and still recorded as succeeded.
  • Ran the new writes against moto's DynamoDB: the ADD counters with a Decimal cost, the conditional list_append with size(), and the nested phases_summary update. The preview stopped at 100, 130 rows were stored, and the cost totals and phase counts were exact.
  • tsc -b and vite build pass.
  • ruff reports no new findings on the changed files.

pipeline_service.py is also touched by #52 and #53, in different functions. A trivial rebase may be needed depending on merge order.

This came out of an inventory of DynamoDB access patterns done for the #44 review. It is independent of #44.

🤖 Generated with Claude Code

Every agent call appended an entry to the run item's agents list. A run can
last 45 minutes with a fan-out of 8, so a few hundred calls together with
200 log lines of CJK text and a 24,000-character result could pass
DynamoDB's 400 KB. The append then failed and took the run down with it.

Each agent call is now its own item in the run's partition
(PK=PIPELINERUN#<id>, SK=AGENT#<ts>#<rand>). The run item gets atomic
agents_total and cost_usd_total counters and keeps the first 100 calls as a
preview, enforced by a size() condition, so run lists cost the same as
before. GET /pipeline-runs/{id} returns every call. When a run ends, its
per-phase stats are computed over all calls and stored as phases_summary,
and the Insights summary view reads that. The result and logs are capped in
UTF-8 bytes (72 KB and 96 KB) instead of characters.

The Pipeline page loads a large run's full call list when its card is
opened, and shows the exact cost total.

Service-entry results were capped at 200,000 characters, up to 600 KB of
CJK. The final write could then fail, and the poller saw a successful call
as failed. They are now capped at 300,000 bytes, with result_truncated
reported to the caller.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@odinwang

Copy link
Copy Markdown
Contributor

Thanks! This change is included in #66 (published together with the related platform fixes). This repository is now updated by snapshot PRs only, so closing this one.

@odinwang odinwang closed this Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants