[ 
https://issues.apache.org/jira/browse/TIKA-4793?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18104392#comment-18104392
 ] 

ASF GitHub Bot commented on TIKA-4793:
--------------------------------------

tballison commented on PR #3009:
URL: https://github.com/apache/tika/pull/3009#issuecomment-5279514561

   Updated review. Let me know if any of these points do not check out.
   
   ```
     Verdict
   
     The BoundedOutputStream rewrite (19c6b1dc4f) is the right architecture and 
resolves most of the earlier feedback — 7 of 9 items including Nicholas's 
fallback-frame issue. But it's
     not mergeable yet: two blockers you've already flagged, one confirmed 
data-loss regression vs main, and the PR body/title now describe code that 
doesn't exist.
   
     Blockers
   
     1. writeCrash — confirmed by reproduction, not just analysis 
(ServerProtocolIO.java:166-175). Truncates to limit/2 chars then calls 
unbounded toBytes() — the only write path that
     bypasses the BoundedOutputStream. Smile is UTF-8, so ≥U+0800 chars encode 
at 3 bytes/char. Repro: limit=1024, CJK exception message → 1498-byte frame → 
client throws
     PayloadLimitExceededException, and the server hung in awaitAck() until 
connection close. Downstream, the crash is misreported as 
PAYLOAD_LIMIT_EXCEEDED (TASK_EXCEPTION category)
     instead of OOM/TIMEOUT (PROCESS_CRASH) — wrong category for retry logic, 
crash detail lost. Javadoc claims "always fits." No test covers it. Fix: route 
through the same
     BoundedOutputStream+fallback as writeFinished, pin with a test.
   
     2. OOM swallowed post-serialization. writeFinished now allocates up to ~3× 
limit transiently (BAOS doubling + toByteArray() copy) on the main-loop thread. 
An OOM there isn't an
     IOException, so it lands in pre-existing catch (Throwable) blocks 
(PipesServer.java:404, ConnectionHandler.java:183) and the JVM keeps running 
post-OOM — per-client mode loops on;
     shared mode stays corrupted for all clients. Violates fork-and-die. 
Pre-existing catch sites, but this PR makes the allocation reachable, so it 
should carry the fix (rethrow
     Error/exit-on-OOM there, and/or -XX:+ExitOnOutOfMemoryError on the forked 
JVM). Side note: buf.writeTo(output) instead of toByteArray() would drop one 
full copy.
   
     Confirmed regression vs main
   
     3. Oversized docs vanish from audit output with 
emitIntermediateResults=true. The static fallback has null emitData/emitKey. On 
main, the client-side rejection path went through
     buildFatalResult, attaching emitKey + intermediate metadata, so a failure 
record was emitted. Now AsyncEmitter.add warn-skips the null emitData — no 
trace of the document in emitter
     output (PipesClient.java:418-424, AsyncEmitter.java:107-113). Fix: in the 
FINISHED branch, when status is PAYLOAD_LIMIT_EXCEEDED with null emitData, 
rebuild via buildFatalResult like
     the exception path.
   
     Your #1 (success clobbering) — still open, structurally foreclosed
   
     Oversized EMIT_SUCCESS_PASSBACK/EMIT_SUCCESS_PARSE_EXCEPTION (already 
emitted to S3/ES) still gets replaced wholesale by the failure-category 
fallback → double-emit on retry. The
     pre-serialized static frame can't carry the original status, so "degrade 
emitData only, keep the status" was never implemented. Fix: retry serialization 
with emitData stripped but
     status kept; static frame only if even that overflows. (Not a regression 
vs main, but it was the ask.)
   
     Diagnosability (usability)
   
     The new path gives the operator less than the old teardown did: the WARN 
logs only the configured limit — no doc/emit key, no actual size (the 
BoundedOutputStream knows it at abort)
     — and nothing anywhere names the maxIpcPayloadBytes knob. Old path at 
least logged "length X exceeds maximum Y" with the doc id. Cheap fix: enrich 
the WARN; serialize a per-doc
     message when the limit has headroom. Also: shared-server mode has 
independently configured client/server limits — server limit > client limit 
reproduces the old teardown; docs don't
     say to keep them in sync.
   
     Docs / hygiene
   
     - PR body and title are stale: they describe the abandoned three-layer 
guard, an "archive sizing fix" (×2→×1) that was reverted (final code keeps ×2; 
only real change is 2→2L
     overflow widening), and a test plan listing tests that don't exist. Author 
should rewrite both.
     - configuration.adoc:158 -Xmx sentence is backwards: says "lower -Xmx to 
~3× this value"; should be "at least ~3×."
     - CHANGES.txt: no TIKA-4793 entry; both the knob and the behavior change 
(clean status vs teardown) belong there.
     - Validation floor admits guaranteed-broken values (floor = 67-byte 
fallback frame; anything below a typical FetchEmitTuple makes every request die 
as undiagnosable
     UNSPECIFIED_CRASH) — a pragmatic floor or load-time WARN would close the 
footgun the docs currently just describe.
     - awaitAck() still reads with the hardcoded 100 MB default 
(ServerProtocolIO.java:184) — harmless for empty ACK frames, inconsistent with 
the bidirectional contract.
     - Minor: BoundedOutputStream int-overflow guard ((long) buf.size() + len), 
dead serializedSize() helper + stale "estimate formula" comment in the test, 
stray blank line in
     TikaPipesConfigTest, writeIntermediate javadoc promises a "full result" 
FINISHED that will almost certainly be the fallback.
   
     What checks out
   
     Rebase is clean (merge-base = current main, real diff 9 files +357/−28); 
no estimate-based pre-check or OOM-catch remains in serialization; overflow 
aborts before any wire byte, no
     desync possible on the bounded paths; exactly-at-limit is symmetric 
writer/reader; Nicholas's minimum-limit fix is complete (enforced in both 
setter and constructor, config load goes
     through the setter, test now reads with the configured limit); 
PAYLOAD_LIMIT_EXCEEDED is handled sanely by tika-server, tika-grpc, 
AsyncProcessor; single-threaded use of
     ServerProtocolIO per connection, no sync issues; config can't disable the 
guard.
   ```




> Make the Pipes IPC max payload size configurable (currently hard-coded to 100 
> MB)
> ---------------------------------------------------------------------------------
>
>                 Key: TIKA-4793
>                 URL: https://issues.apache.org/jira/browse/TIKA-4793
>             Project: Tika
>          Issue Type: Improvement
>          Components: tika-pipes
>            Reporter: Srinivasarao Daruna
>            Priority: Major
>
> PipesMessage.MAX_PAYLOAD_BYTES (tika-pipes-core) is a compile-time constant 
> set to 100 MB:
> // 
> tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/protocol/PipesMessage.java:44
> public static final int MAX_PAYLOAD_BYTES = 100 * 1024 * 1024;
> This cap is enforced on the read side of every IPC message in the 
> PipesClient↔PipesServer socket protocol. It does not limit the file size 
> being parsed (files are fetched server-side by a Fetcher); it limits the size 
> of the serialized JSON payload — most critically the PipesResult (parsed 
> metadata + extracted text) returned in FINISHED messages.
> Problems with the current implementation:
> 1. Hard-coded, not configurable. Users with very large documents that produce 
> large parse results (and no MetadataWriteLimiterFactory configured) have no 
> way to raise the cap short of forking the code. There is no corresponding 
> field in PipesConfig.
> 2. No write-side guard. PipesMessage.write() applies no limit before writing. 
> When the server serializes a PipesResult exceeding 100 MB and sends it, the 
> client's PipesMessage.read() throws IOException("Payload length X exceeds 
> maximum of 104857600 bytes"). This is caught by the catch-all Exception block 
> in PipesClient.waitForServer() and surfaced to the caller as 
> UNSPECIFIED_CRASH — a misleading status that provides no indication of the 
> root cause.
> Proposed fix:
> 1. Add maxIpcPayloadBytes to PipesConfig with a default of 100 * 1024 * 1024, 
> loaded from the "pipes" JSON config section (consistent with all other 
> PipesConfig fields).
> 2. Thread the configured value through to both PipesMessage.read() and 
> PipesMessage.write(), replacing the hard-coded constant.
> 3. Add a write-side guard in PipesMessage.write() so oversized results are 
> caught server-side with a descriptive IOException rather than failing 
> silently at the client with UNSPECIFIED_CRASH.
> Example config (proposed):
> {
>   "pipes": {
>     "maxIpcPayloadBytes": 209715200
>   }
> }
> Note: Users hitting this limit should first consider configuring a 
> MetadataWriteLimiterFactory to bound extracted-text size, which is the right 
> long-term solution for very large documents. But the limit should still be 
> configurable for cases where the full content is legitimately needed.
> Affected files:
> - 
> tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/protocol/PipesMessage.java
> - 
> tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/PipesConfig.java
> - 
> tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/PipesClient.java
> - 
> tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/server/PipesServer.java



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to