Shared Server Mode (YOLO Mode)

Shared Server Mode is an experimental, high-risk option that trades reliability for reduced memory usage. It is not the default and should only be enabled when you fully understand the limitations.

Overview

By default, Tika Pipes runs each PipesClient with its own dedicated PipesServer JVM process. This provides isolation: if one document causes a crash, OOM, or timeout, only that single request is affected.

Shared Server Mode changes this model: all clients connect to a single shared server process. This saves memory (N-1 JVMs worth) but means that one failure affects all in-flight requests.

Architecture Comparison

Default Mode (Per-Client):              Shared Mode (YOLO):
──────────────────────────              ────────────────────
PipesClient-0 → Server-0 (JVM)          PipesClient-0 ─┐
PipesClient-1 → Server-1 (JVM)          PipesClient-1 ─┼→ Shared Server (1 JVM)
PipesClient-2 → Server-2 (JVM)          PipesClient-2 ─┤   with N connection handlers
PipesClient-3 → Server-3 (JVM)          PipesClient-3 ─┘

Memory: 4 JVMs                          Memory: 1 JVM
Isolation: Per-request                  Isolation: None (shared fate)

Performance

Shared mode is not only a memory option. Because all requests are served by one warm JVM — a single JIT profile and a single garbage collector — it typically has higher throughput than per-client mode at the same concurrency, and can show lower tail latency (one large heap incurs fewer, shorter GC stalls than several small worker heaps). In one benchmark it came within roughly 5–15% of a single in-JVM parser’s throughput on mixed and large documents, where per-client was ~20–35% slower. This throughput edge is the upside you weigh against the loss of per-request isolation described below. See Performance and Isolation Trade-offs for the full comparison.

Limitations and Risks

Shared fate

Any crash, timeout, or OutOfMemoryError kills the one server process, so all concurrent requests are lost, not just the offending one. The server is then restarted and clients reconnect (see Recovery Behavior).

Shared resources

All concurrent parses share one heap, CPU allocation, and file-handle table. A memory-hungry document can starve the others or trigger the OOM that kills everything, and there is no per-document heap limit to contain it — you must size the heap for worst-case concurrent load rather than worst-case single document.

Concurrency exposure

In per-client mode each parse gets its own JVM and a single thread, so thread-safety bugs in parsers stay hidden. In shared mode parses run concurrently in one JVM, which can expose latent threading bugs in parsers or their dependencies. Strange behaviour that appears only in shared mode is most often this.

When to Use Shared Mode

Consider shared mode only when all of these hold:

  • You have strict memory constraints and cannot run N separate JVMs

  • Your documents are well-behaved and unlikely to cause OOM or timeouts

  • You can tolerate occasional loss of multiple in-flight requests

  • You have tested thoroughly with your specific document corpus

Configuration

Enable shared mode by setting useSharedServer to true in your pipes configuration:

{
  "pipes": {
    "numClients": 4,
    "useSharedServer": true,
    "forkedJvmArgs": ["-Xmx4g"]
  },
  "parse-context": {
    "timeout-limits": {
      "progressTimeoutMillis": 60000
    }
  }
}

See Timeouts for details on configuring timeouts.

Sizing Guidance

Size the single heap for numClients × worst-case per-document usage, plus GC headroom and JVM overhead: 4 clients at up to 500 MB each needs at least 2 GB. That is the same total a per-client deployment would use, but concentrated in one JVM where a single OOM takes down every in-flight request instead of one.

For the per-fork -Xmx rule of thumb that informs both modes, see Heap per fork.

Recovery Behavior

On a fatal error (OOM, timeout, or crash) the affected client marks the server for restart and the process is terminated; other clients detect the pending restart, and once a new server process is up they reconnect and resume. All in-flight requests from all clients are lost in that window, so retry logic belongs at the application level.

Recommendation

Use the default per-client mode for production workloads unless a memory constraint forces otherwise — the isolation is worth more than the saved JVMs. If you must use shared mode, test against your own corpus, monitor OOM and timeout events, retry lost requests at the application level, and size the heap generously.