Skip to content

mark() refuses unrelated #[Cacheable] siblings later in the same request #234

Closed

Description

@koriym

A mark() refuses every #[Cacheable] write later in the same request, including unrelated siblings.

Behaviour (1.x 062bbde, found in the 1.18.0 release review)

A #[DonutCache] page embeds a hole that issues a CSRF token (UncacheableMarkInterface::mark()) and an unrelated #[Cacheable] hole. When the unrelated hole is evaluated after the mark, its own write is refused (put_skipped{reason: unstorable}) and its onGet() re-runs on every such request. Evaluated before the mark, or fetched in a request without a mark, it is stored normally; there is no lasting poisoning (verified: the next unmarked request stores it, the one after hits).

Cause

QueryRepository::doPut() asks UnstorableGateInterface::reason(), which uses UncacheableMarkInterface::isMarked() (any owner, anywhere in the request). This is deliberately conservative and documented: QueryRepository cannot see the resource call tree, and a #[Cacheable] parent that embeds a marked child must be refused. Only a #[DonutCache] write's own template uses the scoped isMarkedForCurrentScope().

Direction

Refuse only the ancestor chain of the marking resource (track the executing resource as a scope stack around each #[Cacheable] execution; a write is refused when a mark was made in its own subtree). Must keep every #222 guarantee, incl. render-time marks and nested holes (see #231's review rounds).

Workaround until then: place cacheable siblings before the marking hole in the template, so they are evaluated (and stored) before the mark.

Activity

  1. koriym commented on Oct 28, 2026

    @koriym
    MemberAuthor

    Closing as obsolete: #242 removed the unreleased mark() machinery (UncacheableMarkInterface, UnstorableGate, ...) entirely, so there is no request-wide mark left to refuse unrelated #[Cacheable] siblings. In 1.18.0, QR trusts the cache attribute as the declaration that a response is shared, and per-user values belong in a non-cacheable #[DonutCache] hole (see the README).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions