Skip to content

[FEATURE] Add worker-local document pool to skip XML re-parsing - #2212

Open
stweil wants to merge 1 commit into
kitodo:mainfrom
stweil:kitodo/doc-cache-pool
Open

stweil wants to merge 1 commit into
kitodo:mainfrom
stweil:kitodo/doc-cache-pool

Conversation

@stweil

@stweil stweil commented Oct 16, 2026

Copy link
Copy Markdown
Member

Reading a document from the file-based tx_dlf_doc cache requires deserializing the entry and re-parsing the entire METS XML in MetsDocument::__wakeup(), and every cache hit then re-runs the lazy data loaders (physical structure, structure links, metadata array, file groups). For documents that are requested repeatedly by the same PHP-FPM worker - typical for crawlers, and for pages that fetch the same document several times, e.g. periodika issue lists - this is the dominant CPU cost.

Keep a short-lived, per-worker in-memory pool of recently used documents in DocumentCacheManager. A pool hit skips the file cache, the deserialization, the XML re-parsing and the lazy reloading entirely and reuses the already computed data of the document.

The pool is a static array (one per FPM worker process) with a sliding TTL of 300 s and an entry cap of 5 documents, evicting the least recently used entry first. Because a PHP-FPM worker handles only one request at a time, sharing instances between sequential requests of the same worker is safe.

set(), remove() and flush() keep the pool in sync with the file cache, including the LOAD_FAILED sentinel.

Unit tests cover pool hits, TTL expiration, LRU eviction, pool filling from the file cache and pool synchronization by setFail(), remove() and flush().

Assisted-by: OpenCode / qwen3.8-27b-thinking (Alibaba Cloud)

@sebastian-meyer sebastian-meyer added the ↷ feature A new feature or enhancement. label Oct 16, 2026
@sebastian-meyer sebastian-meyer changed the title [TASK] Add worker-local document pool to skip XML re-parsing [FEATURE] Add worker-local document pool to skip XML re-parsing Oct 16, 2026
@stweil
stweil force-pushed the kitodo/doc-cache-pool branch from 6e1fbba to 12c1f1e Compare October 19, 2026 16:39
Reading a document from the file-based tx_dlf_doc cache requires
deserializing the entry and re-parsing the entire METS XML in
MetsDocument::__wakeup(), and every cache hit then re-runs the
lazy data loaders (physical structure, structure links, metadata
array, file groups). For documents that are requested repeatedly by
the same PHP-FPM worker - typical for crawlers, and for pages that
fetch the same document several times, e.g. periodika issue lists -
this is the dominant CPU cost.

Keep a short-lived, per-worker in-memory pool of recently used
documents in DocumentCacheManager. A pool hit skips the file cache,
the deserialization, the XML re-parsing and the lazy reloading
entirely and reuses the already computed data of the document.

The pool is a static array (one per FPM worker process) with a
sliding TTL of 300 s and an entry cap of 5 documents, evicting the
least recently used entry first. Because a PHP-FPM worker handles
only one request at a time, sharing instances between sequential
requests of the same worker is safe.

set(), remove() and flush() keep the pool in sync with the file
cache, including the LOAD_FAILED sentinel.

Unit tests cover pool hits, TTL expiration, LRU eviction, pool
filling from the file cache and pool synchronization by setFail(),
remove() and flush().

Since the pool is per-worker state, it would survive individual
functional tests and leak a document loaded by one test (possibly
with different settings) into the next one. A resetPool() method
clears the pool without touching the file cache; the functional test
base class calls it for each test case, mirroring the existing
Helper::resetIndexNameCache() and Indexer::resetProcessedDocs()
test isolation pattern.

Assisted-by: OpenCode / qwen3.8-27b-thinking (Alibaba Cloud)
Signed-off-by: Stefan Weil <sw@weilnetz.de>
@stweil
stweil force-pushed the kitodo/doc-cache-pool branch from 12c1f1e to 13c2d7a Compare October 26, 2026 11:53

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

↷ feature A new feature or enhancement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants