Skip to content

Commit 3d5bd05

Browse files
authored
TIKA-4813: unified timeout model (#3005)
1 parent f1f7532 commit 3d5bd05

105 files changed

Lines changed: 3407 additions & 556 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/modules/ROOT/pages/advanced/setting-limits.adoc

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -256,48 +256,43 @@ since the parser last reported progress.
256256

257257
|`totalTaskTimeoutMillis`
258258
|3600000 (1 hour)
259-
|Maximum wall-clock time in milliseconds for the entire parse task.
259+
|Maximum wall-clock time in milliseconds for the entire parse task, including all of
260+
its embedded documents. Every per-parser timeout is clipped to whatever remains of this
261+
budget, no matter how it is itself configured.
260262

261263
|`progressTimeoutMillis`
262264
|120000 (2 minutes)
263-
|Maximum time in milliseconds since the parser last reported progress. Catches
264-
infinite loops and hung processes.
265+
|Maximum time in milliseconds the task may go silent before it is considered hung and
266+
killed. Catches infinite loops and hung processes.
267+
268+
|`throwOnDeadline`
269+
|false
270+
|Whether a `totalTaskTimeoutMillis` exhaustion should throw `EmbeddedLimitReachedException`
271+
instead of skipping remaining embedded documents cleanly and reporting a
272+
`PARTIAL_TIMEOUT` result. See xref:pipes/timeouts.adoc#_throwondeadline_failing_hard_instead_of_partial[throwOnDeadline]
273+
for the pipes-mode caveat before enabling this.
265274
|===
266275

267276
[IMPORTANT]
268277
====
269-
Which bound actually applies depends on whether the parser reports progress.
270-
A parser that never calls `TikaProgressTracker.update()` never advances the
271-
timer, so it effectively gets `progressTimeoutMillis` as its total timeout —
272-
matching the single-timeout behavior of earlier versions.
273-
274-
In practice only long-running parsers report progress: `TesseractOCRParser`,
275-
`Tess4JParser`, `ExternalParser`, `GDALParser`, the VLM and image-embedding
276-
parsers, and `StringsParser`. Everything else — including container parsing and
277-
embedded-document recursion — does not.
278-
279-
Those parsers report progress *after* each external-process invocation
280-
completes, not while one is running. So a document that needs many OCR calls
281-
can extend well past `progressTimeoutMillis`, because each finished page resets
282-
the timer — but a *single* call that runs longer than `progressTimeoutMillis`
283-
is still cut short.
284-
285-
Because of this, `progressTimeoutMillis` also caps how long any single external
286-
process may run. Parsers that spawn processes size their own timeout via
287-
`TimeoutLimits.getProcessTimeoutMillis(context, ...)`, which never allows a
288-
value beyond `progressTimeoutMillis`, so the process is stopped just before the
289-
progress watchdog would fire.
290-
291-
The shipped defaults are aligned: `progressTimeoutMillis` is 120 seconds and
292-
the bundled process-spawning parsers (OCR, strings, inference) each default to
293-
a 120-second per-process timeout, so those defaults are reachable. **If you
294-
raise a per-process timeout above 120 seconds, raise `progressTimeoutMillis`
295-
to match** — raising the parser's own timeout alone has no effect.
296-
297-
For most documents — anything without one of the parsers above in the chain —
298-
the effective ceiling is `progressTimeoutMillis`, not `totalTaskTimeoutMillis`.
299-
Lower `totalTaskTimeoutMillis` if you need a hard ceiling on OCR-heavy or
300-
external-process work regardless of progress.
278+
The two bounds are independent, not tiered: `progressTimeoutMillis` is a *stall
279+
detector*, not a ceiling on any single operation's runtime. A per-parser timeout (e.g.
280+
`tesseract-ocr-parser.timeoutMillis`) is honored up to `min(configured, time remaining
281+
in totalTaskTimeoutMillis)` — `progressTimeoutMillis` never enters that calculation.
282+
283+
While Tika waits on a bounded external call (an OCR process, an external parser), the
284+
wait itself checkpoints progress periodically, so a single legitimate call may run far
285+
longer than `progressTimeoutMillis` without tripping the stall detector — a 10-minute
286+
`readpst` invocation does not need `progressTimeoutMillis` raised to 10 minutes. The
287+
stall detector only fires on genuine silence: an in-JVM hang, or a wedged process that
288+
even checkpointing cannot report past.
289+
290+
Budgets compose recursively regardless of nesting: a PDF inside a zip inside an email
291+
draws every operation, at any depth, from the same `totalTaskTimeoutMillis` budget for
292+
that top-level task.
293+
294+
For the full mental model, worked scenarios, and how each outcome is reported (metadata
295+
flags, result status, exception messages), see xref:pipes/timeouts.adoc[Timeouts].
301296
====
302297

303298
=== JSON Configuration
@@ -321,6 +316,7 @@ Configuration file: `tika-serialization/src/test/resources/configs/timeout-limit
321316
[source,java]
322317
----
323318
TimeoutLimits limits = new TimeoutLimits(7200000, 120000);
319+
limits.setThrowOnDeadline(false);
324320
context.set(TimeoutLimits.class, limits);
325321
326322
// Helper method

docs/modules/ROOT/pages/migration-to-4x/migrating-tika-server-4x.adoc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,13 @@ The `/tika` endpoint no longer routes based on `Accept` headers. Use explicit pa
172172
173173
The following `TikaServerConfig` options have been removed:
174174
175-
* `taskTimeoutMillis` - Now configured via `parse-context.timeout-limits.progressTimeoutMillis` (and optionally `totalTaskTimeoutMillis`); see xref:pipes/timeouts.adoc[Timeouts].
175+
* `taskTimeoutMillis` - Was the total time allowed per task before the forked process was
176+
killed; that is now `parse-context.timeout-limits.totalTaskTimeoutMillis` (same
177+
meaning, new home). 4.x also adds a second, independent axis with no pre-4.0
178+
equivalent -- `progressTimeoutMillis`, a stall detector that kills the task only after
179+
a period of genuine silence, not merely after `totalTaskTimeoutMillis` of wall-clock
180+
time -- worth setting deliberately rather than leaving at its default. See
181+
xref:pipes/timeouts.adoc[Timeouts].
176182
* `taskPulseMillis` - No longer needed
177183
* `minimumTimeoutMillis` - No longer needed
178184

0 commit comments

Comments
 (0)