---
title: "Performance considerations"
manual: "Temporal Cache Management"
version: "main"
source: "Performance/Index.rst"
rendered: "2026-10-07T14:40:31+00:00"
---

# Performance considerations {#performance-considerations}

## Overview {#performance-overview}

The extension buys correct temporal behavior by making page cache entries expire earlier
than they otherwise would, or by flushing them from a background task.
Both cost cache hits.
How many depends entirely on which scoping and timing strategy is configured, and the
default is the most expensive combination.

> [!IMPORTANT]
> The default configuration — `scoping.strategy = global` and
> `timing.strategy = dynamic` — shortens the lifetime of **every** page cache entry to
> the earliest upcoming transition anywhere on the site.
> One scheduled page therefore expires the whole site's page cache at that moment.

This chapter describes what each configuration actually does, so the cost can be measured
on a specific site.
It contains no benchmark figures: none have been measured for this extension, and numbers
from another site would not transfer.

## The model in one table {#performance-model}

Scoping and timing are independent, and scoping answers two different questions depending
on which timing strategy reads it.

| Scoping | with `dynamic` timing (shortens lifetimes) | with `scheduler` timing (flushes tags) |
| --- | --- | --- |
| `global` | Every entry expires at the earliest transition site-wide. | Empties the entire page cache, once per transition. |
| `per-page` | An entry expires at the earlier of: the next `pages` transition site-wide, or the next transition of a content element on that page. | Flushes `pageId_<uid>` for a page, `pageId_<pid>` for a content element. |
| `per-content` | Same as `global` — this strategy does not narrow lifetimes. | Flushes one `pageId_*` tag per page that `sys_refindex` reports for the element. |

Read the table before choosing a strategy.
Two combinations are commonly misread:

-   `per-content` \+ `dynamic` narrows **nothing**.
    Its precision lives in the flush tags, which `dynamic` timing never reads.
    Configuring it without switching timing gives global behavior plus refindex code that
    never runs.
-   `per-page` or `per-content` \+ `scheduler` flushes only the tags named above.
    A page transition then refreshes that page, not the menus on every other page.
    Only `global` scoping refreshes those.

`hybrid` timing picks per record type: `timing.hybrid.pages` decides the lifetime
calculation and page transitions, `timing.hybrid.content` decides content transitions.

## Where the cost sits {#performance-cost}

-   **Query cost with `dynamic` timing**

    Every page cache write runs two `MIN()` queries per monitored table — four with the
    default `pages` and `tt_content`, two more for every table another extension
    registers.
    Each query is a single indexed aggregate returning one integer.
    One request carries one workspace and one language, so the query count does not grow
    with the number of configured languages.

-   **Cache hit ratio**

    This is the cost that matters, and it is a property of the site, not of the extension:
    how often transitions occur, how many pages exist, and how much traffic arrives between
    two transitions.

-   **Simultaneous expiry**

    With `global` scoping every entry carries the same expiry timestamp, so they all miss
    at once.
    Behind a CDN or reverse proxy that miss propagates upstream.
    `per-page` scoping spreads the timestamps for content transitions; page transitions
    still land on all entries at once.

-   **Query cost with `scheduler` timing**

    Zero during page generation.
    The cost moves into the scheduled task, which is heavier than the lookup it replaces:
    each run loads **every** record carrying a `starttime` or `endtime` from every
    monitored table into PHP — one query per table, no time restriction in SQL — and then
    filters the run's time window in PHP.
    Its cost therefore scales with the total amount of temporal content on the site, not
    with the number of transitions in the window.
    Each transition that does fall in the window costs one `flushByTag()` per tag the
    scoping strategy names.

## Chapters {#performance-chapters}

**⚡ Optimization strategies**

What scoping, timing and harmonization each change, and how to combine them.

[Read Strategies](Strategies.html#performance-strategies)

**⚠️ Limitations**

What the approach cannot do, and the failure modes to plan for.

[Read Limitations](Limitations.html#performance-limitations)

**🎯 Decision guide**

Which configuration matches which shape of site, and what to measure before
deciding.

[Read Decision Guide](DecisionGuide.html#decision-guide)

**🔄 Alternative approaches**

Solving temporal content without this extension: uncached menus, ESI, client-side
loading, scheduled clearing.

[Read Alternatives](Alternatives.html#performance-alternatives)

**❓ Frequently asked questions**

Cache synchronization, CDNs, workspaces, and when not to use the extension.

[Read FAQ](FAQ.html#performance-faq)

## Related documentation {#performance-related}

-   [Architecture](../Architecture/Index.html#architecture) — the implementation behind this behavior
-   [Configuration](../Configuration/Index.html#configuration) — every option in detail
-   [Installation](../Installation/Index.html#installation) — installation and index verification
-   [Approach, limits and a core solution](../Phases/Index.html#phases) — why the approach is shaped this way
