Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Move element/query recording out of the TemplateCaches service #1507

Closed
brandonkelly opened this issue Mar 13, 2017 · 3 comments
Closed

Move element/query recording out of the TemplateCaches service #1507

brandonkelly opened this issue Mar 13, 2017 · 3 comments
Assignees
Labels
enhancement improvements to existing features extensibility 🔌 features related to plugin/module dev
Milestone

Comments

@brandonkelly
Copy link
Member

That way plugins could initiate their own template recorders, and use the resulting collections for their own purposes, unrelated to Craft's own caching system (for example, they could be used as Varnish dependencies).

@brandonkelly brandonkelly added the enhancement improvements to existing features label Mar 13, 2017
@brandonkelly brandonkelly self-assigned this Mar 13, 2017
brandonkelly added a commit that referenced this issue May 23, 2020
@brandonkelly brandonkelly added the extensibility 🔌 features related to plugin/module dev label May 23, 2020
@brandonkelly brandonkelly added this to the 3.5 milestone May 23, 2020
@brandonkelly
Copy link
Member Author

brandonkelly commented May 23, 2020

This is resolved for Craft 3.5. (8859124)

  • Element queries now register cache invalidation tags based on the parameters that are set on them

    protected function cacheTags(): array
    {
    $tags = [];
    // If the type is set, go with that instead of the section
    if ($this->typeId) {
    foreach ($this->typeId as $typeId) {
    $tags[] = "entryType:$typeId";
    }
    } else if ($this->sectionId) {
    foreach ($this->sectionId as $sectionId) {
    $tags[] = "section:$sectionId";
    }
    }
    return $tags;
    }

  • Element types now define the tags that should be invalidated when they are saved/deleted

    cms/src/elements/Entry.php

    Lines 839 to 852 in 8859124

    public function getCacheTags(): array
    {
    $tags = [
    "entryType:$this->typeId",
    "section:$this->sectionId",
    ];
    // Did the entry type just change?
    if ($this->typeId != $this->_oldTypeId) {
    $tags[] = "entryType:$this->_oldTypeId";
    }
    return $tags;
    }

  • Anything that wants to start caching things that involve element queries can do so by calling craft\services\Elements::startCollectingCacheTags() before executing the queries, and stopCollectingCacheTags() afterwards, which will return a yii\caching\TagDependency object that can be used as a cache dependency.

We’re using this new system for template caches ({% cache %} tags) and GraphQL query caches. Here’s the GraphQL code, as that’s a simpler example, since it doesn’t have to worry about nested caches like template caches do:

cms/src/services/Gql.php

Lines 417 to 440 in 8859124

if ($cacheKey && ($cachedResult = $this->getCachedResult($cacheKey)) !== null) {
$event->result = $cachedResult;
} else {
$schemaDef = $this->getSchemaDef($schema, $debugMode || StringHelper::contains($query, '__schema'));
$elementsService = Craft::$app->getElements();
$elementsService->startCollectingCacheTags();
$event->result = GraphQL::executeQuery(
$schemaDef,
$query,
$event->rootValue,
$event->context,
$event->variables,
$event->operationName,
null,
$this->getValidationRules($debugMode)
)->toArray($debugMode);
$dep = $elementsService->stopCollectingCacheTags();
if (empty($event->result['errors']) && $cacheKey) {
$this->setCachedResult($cacheKey, $event->result, $dep);
}
}

cms/src/services/Gql.php

Lines 478 to 488 in 8859124

public function setCachedResult(string $cacheKey, array $result, TagDependency $dependency = null)
{
if ($dependency === null) {
$dependency = new TagDependency();
}
// Add the global graphql cache tag
$dependency->tags[] = self::CACHE_TAG;
Craft::$app->getCache()->set($cacheKey, $result, null, $dependency);
}

@Mosnar
Copy link
Contributor

Mosnar commented May 25, 2020

@brandonkelly Great work! Just a heads-up, I was playing with this and if you don't create a query within the cache tag (for example, using the matched element (entry.title) instead, the cache is not automatically invalidated when the corresponding entry saves.

Does not clear on entry save:

{% cache %}
{{ entry.title }}
{% endcache %}

Does clear on entry save:

{% cache %}
    {% set entry = craft.entries.section('homepage').one() %}
    <h1>{{ entry.test }}</h1>
{% endcache %}

@brandonkelly
Copy link
Member Author

@Mosnar I caught that yesterday as well. Should be fixed via decc331.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
enhancement improvements to existing features extensibility 🔌 features related to plugin/module dev
Projects
None yet
Development

No branches or pull requests

2 participants