Repository navigation
improvement(executor): size payload compaction bottom-up instead of re-serializing every subtree - #9316
Conversation
…e-serializing every subtree Each compacted node now carries its JSON byte size, so a parent sums its children instead of stringifying its whole subtree again, and the walk stays synchronous until something spills. A node still serializes for real where a toJSON makes the sum unsound.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
There was a problem hiding this comment.
No issues found across 2 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Heads up: you’re close to your flex budget. Increase your flex budget so reviews don’t pause.
Turn on auto-fix | Re-trigger cubic
[Medium impact] The PR appears safe to merge, with no new actionable issues found. SummaryThe PR sizes payloads bottom-up and avoids promises when no asynchronous work is needed.
Reviews (2) · Last reviewed commit: "test(executor): pin the subflow aggregat..." · Reviewed by Greptile |
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 2 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Heads up: you’re close to your flex budget. Increase your flex budget so reviews don’t pause.
Turn on auto-fix | Re-trigger cubic
…evels (#9319) The bottom-up compaction from #9316 walks a tree on one synchronous stack until something spills, so deep trees (and every rejectLargeValues tree past ~1,000 levels) overflow where the old awaiting walk compacted them. Every 256 nesting levels the walk now resumes from a microtask; later siblings wait for the resumed subtree to finish walking, so objects are first seen in the same order and the output and stored values stay identical. Adds regression tests for 10,000-level plain and 8,000-level Date-per-level trees and for a shared object first reached inside a resumed subtree.
Summary
compactValuere-ranJSON.stringifyon every subtree at every level and allocated a promise per node, so compaction was O(size × depth) on every block output, loop iteration/exit, parallel aggregate, and route response — all microtasks, never yieldingtoJSONmakes the sum unsoundJSON.stringifyexactly where the old measurement could differ from a sum: its owntoJSON, a child withtoJSON(which receives the key it is serialized under), and a reject-mode__proto__key (assignment sets the prototype)seenset order, so shared references resolve identically), and a synchronous throw still observes already-pending siblings so none reject unhandledcompactValue/compactEntries/early-reject functions are rewritten in place with the same branches, reuse checks, reject estimates, manifests, and user-file strippingcompactSubflowResultssums the aggregate{ results }size from the per-result sizes instead of stringifying the whole aggregateType of Change
Testing
compactExecutionPayload,compactSubflowResults,compactBlockOutput, andcompactBlockLogs. Compares values, input identity, prototypes, own-key order and enumerability, store calls (json + size), and errors — 0 mismatches. Covers toJSON (own, keyed, on functions), getters, Symbol keys, non-enumerables, holes, -0/NaN/Infinity, BigInt, null-proto,__proto__keys, cycles and shared refs, Dates, Buffers, class instances, user files, refs,finalBlockLogs, deep chains, lone surrogates, multibyte and escaped keys, negative/zero thresholds, preserveRoot, durable manifests, reject mode. Mutation-checked: each of 8 seeded accounting bugs fails itobservedBytesmatch exactly forcompactExecutionPayload(the only production reject path). For the concurrent APIs (compactSubflowResults,compactBlockOutput,compactBlockLogs) called in reject mode — which no production caller does — when several branches overflow, which one reports first follows microtask timing, so the byte count can differ; label and maxBytes matchserializer.test.ts: oversized child under an object whosetoJSONserializes small still spills, and rejects in reject mode; a parent is sized by its child's keyedtoJSON; a value exactly at the threshold stays inline and one byte over spills with the exact size (escapes, multibyte, lone surrogates, omitted keys,undefinedin arrays). Pass on the old serializer; the first fails on a top-down fast-path prototype, the others fail on the corresponding accounting mutationspayloads/*.test.ts,loop,parallel,block-executor,executor,engine,execution-core,logging-session,event-buffersuites pass;bun run lint;check:comment-hygiene,check:utils,check:explicit-any,check:source-conventions,check:test-patterns,check:file-names. Full type-check /check:audits/ test suite left to CIChecklist
test-auditauthoring gate)