perf(pii): mask offloaded refs + parallelize + raise redaction concurrency#5436
perf(pii): mask offloaded refs + parallelize + raise redaction concurrency#5436TheodoreSpeaks wants to merge 3 commits into
Conversation
… raise concurrency - Block-output and input stages now hydrate → mask → re-store large-value refs (function/tool outputs are offloaded to refs before reaching the redactor, which treats refs as opaque) — fixes big block outputs coming back unredacted - Parallelize large-value ref hydrate/mask/re-store: collect refs across the whole payload and process them with bounded concurrency instead of one sequential pass per key (PII_REF_CONCURRENCY, default 4) - Raise mask-batch chunk concurrency default 4 -> 64 to saturate the load-balanced Presidio fleet behind the internal ALB (PII_MASK_CHUNK_CONCURRENCY, env-tunable)
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
@greptile review |
PR SummaryHigh Risk Overview
Throughput knobs: default app → mask-batch concurrency 4 → 64 ( Reviewed by Cursor Bugbot for commit 0aa7648. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryThis PR fixes a correctness gap in PII redaction: block outputs that exceed the large-value threshold are offloaded to object-storage refs before reaching the redactor, and the plain string walk treats a ref as opaque — so PII inside those refs was never masked. The fix adds a
Confidence Score: 5/5The change is safe to merge: the correctness fix is well-scoped, the parallel pool uses a total mapper that correctly defers the abort until after all workers drain, and both new test cases pin the security-critical throw-mode path. All three call sites follow the same hydrate → mask → re-store ordering. The mapWithConcurrency contract is satisfied — the mapper never rejects; errors are captured and rethrown after the pool completes. The raised CHUNK_CONCURRENCY default is env-tunable and the existing comment explicitly calls out the single-instance tuning knob. No data-path regressions were found. No files require special attention. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant BE as BlockExecutor / ExecutionCore
participant LV as redactLargeValueRefsInValue
participant CR as collectRefs (sync walk)
participant RR as resolveReplacements (parallel)
participant S3 as Object Storage
participant PR as Presidio (maskPIIBatchViaHttp)
participant RS as redactObjectStrings
BE->>LV: normalizedOutput (may contain LargeValueRefs)
LV->>CR: walk payload, collect all refs
CR-->>LV: refs[]
LV->>RR: "unique refs, bounded concurrency (PII_REF_CONCURRENCY=4)"
loop For each ref (parallel)
RR->>S3: materializeLargeValueRef
S3-->>RR: materialized value
RR->>PR: "maskPIIBatchViaHttp (PII_MASK_CHUNK_CONCURRENCY=64)"
PR-->>RR: masked value
RR->>S3: compactExecutionPayload (re-store masked)
S3-->>RR: new LargeValueRef
end
RR-->>LV: replacements Map
LV->>LV: substituteRefs (sync swap)
LV-->>BE: payload with refs replaced by masked refs
BE->>RS: redactObjectStrings (mask inline strings)
RS-->>BE: fully masked output
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant BE as BlockExecutor / ExecutionCore
participant LV as redactLargeValueRefsInValue
participant CR as collectRefs (sync walk)
participant RR as resolveReplacements (parallel)
participant S3 as Object Storage
participant PR as Presidio (maskPIIBatchViaHttp)
participant RS as redactObjectStrings
BE->>LV: normalizedOutput (may contain LargeValueRefs)
LV->>CR: walk payload, collect all refs
CR-->>LV: refs[]
LV->>RR: "unique refs, bounded concurrency (PII_REF_CONCURRENCY=4)"
loop For each ref (parallel)
RR->>S3: materializeLargeValueRef
S3-->>RR: materialized value
RR->>PR: "maskPIIBatchViaHttp (PII_MASK_CHUNK_CONCURRENCY=64)"
PR-->>RR: masked value
RR->>S3: compactExecutionPayload (re-store masked)
S3-->>RR: new LargeValueRef
end
RR-->>LV: replacements Map
LV->>LV: substituteRefs (sync swap)
LV-->>BE: payload with refs replaced by masked refs
BE->>RS: redactObjectStrings (mask inline strings)
RS-->>BE: fully masked output
Reviews (3): Last reviewed commit: "fix(pii): keep resolveReplacements mappe..." | Re-trigger Greptile |
Greptile SummaryThis PR fixes a correctness gap where PII inside large-value refs (function/tool outputs offloaded to object storage before reaching the redactor) was never sent to Presidio, and simultaneously raises throughput by parallelizing ref hydration and increasing the mask-batch chunk concurrency default from 4 to 64.
Confidence Score: 4/5Safe to merge; the correctness fix is sound and tests cover the critical paths. The one rough edge is a contract tension in the concurrency utility that works today but could surprise a future maintainer. The hydrate→mask→re-store ordering is correct, the shared seen WeakSet prevents duplicate work, and substituteRefs correctly uses Map identity to swap refs. The one concern worth tracking: resolveReplacements passes a mapper that intentionally throws (in throw mode) to mapWithConcurrency, whose documented contract says fn MUST NOT reject. The behavior is currently correct because Promise.all propagates the rejection as intended, but if mapWithConcurrency is ever changed to isolate per-item errors, the abort-on-failure behavior in throw mode would silently stop working. apps/sim/lib/logs/execution/pii-large-values.ts — the resolveReplacements + mapWithConcurrency interaction in throw mode is the one area that deserves a second look. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant BE as BlockExecutor
participant LV as redactLargeValueRefsInValue
participant OS as Object Storage
participant PR as Presidio
participant RS as redactObjectStrings
BE->>LV: normalizedOutput with large-value refs
LV->>LV: collectRefs sync walk
LV->>OS: materializeLargeValueRef parallel x REF_CONCURRENCY
OS-->>LV: materialized values
LV->>PR: maskPIIBatchViaHttp parallel chunks x CHUNK_CONCURRENCY
PR-->>LV: masked values
LV->>OS: compactExecutionPayload re-store as new ref
OS-->>LV: new masked LargeValueRef
LV-->>BE: output with fresh masked refs
BE->>RS: redactObjectStrings inline strings only
RS-->>BE: fully masked output
BE->>BE: compactExecutionPayload final pass
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant BE as BlockExecutor
participant LV as redactLargeValueRefsInValue
participant OS as Object Storage
participant PR as Presidio
participant RS as redactObjectStrings
BE->>LV: normalizedOutput with large-value refs
LV->>LV: collectRefs sync walk
LV->>OS: materializeLargeValueRef parallel x REF_CONCURRENCY
OS-->>LV: materialized values
LV->>PR: maskPIIBatchViaHttp parallel chunks x CHUNK_CONCURRENCY
PR-->>LV: masked values
LV->>OS: compactExecutionPayload re-store as new ref
OS-->>LV: new masked LargeValueRef
LV-->>BE: output with fresh masked refs
BE->>RS: redactObjectStrings inline strings only
RS-->>BE: fully masked output
BE->>BE: compactExecutionPayload final pass
Reviews (2): Last reviewed commit: "perf(pii): mask offloaded refs in block ..." | Re-trigger Greptile |
…rrency contract) - Catch per-ref errors inside the mapWithConcurrency mapper and rethrow the first after the pool drains, instead of letting a throwing mapper reject the pool (the util documents fn MUST NOT reject). Preserves fail-fast abort in throw mode. - Add multi-ref throw-mode test: one of several refs failing aborts the redaction
|
@greptile review |
|
Both re-raised Greptile P2s are already addressed in
|
…RVICE_CHUNK_CONCURRENCY) Was hardcoded to 4; now env-tunable (default 4) so the inner route->Presidio fan-out can scale with the fleet alongside the outer PII_MASK_CHUNK_CONCURRENCY.
Summary
PII_REF_CONCURRENCY, default 4).PII_MASK_CHUNK_CONCURRENCY, env-tunable).Type of Change
Testing
tscclean,bun run lint,check:api-validation:strictpasspii-large-values.test.ts)b11e8f19(function output offloaded to refs, zero Presidio calls → confirmed refs skipped)Checklist