Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Second Level Cache, QueryCache, Eviction #6096

Open
wtorsi opened this issue Oct 21, 2016 · 3 comments
Open

Second Level Cache, QueryCache, Eviction #6096

wtorsi opened this issue Oct 21, 2016 · 3 comments
Assignees

Comments

@wtorsi
Copy link

wtorsi commented Oct 21, 2016

Eviction non builded before queryCache is not allowed, or not performed in DefaultCache.
What is the reason for this limitation?

/**
     * {@inheritdoc}
     */
    public function evictQueryRegion($regionName = null)
    {
        if ($regionName === null && $this->defaultQueryCache !== null) {
            $this->defaultQueryCache->clear();

            return;
        }


        if (isset($this->queryCaches[$regionName])) {
            $this->queryCaches[$regionName]->clear();
        }
    }

According to that limitation, firstly I must create cache before clearing, using getQueryCache($region) from EntityManager::$cache, like below:

public function evictQueryCache(string $region)
    {
        $cache = $this->em->getCache();
        $region = $cache->getQueryCache($region);
        $cache->evictQueryRegion($region);
        $region->clear();
    }

What is the reason for this limitation?

@lcobucci
Copy link
Member

@wtorsi why are you trying to do that? L2C should control eviction automatically (when you manipulate entities ofc).

@wtorsi
Copy link
Author

wtorsi commented Oct 22, 2016

Ok, I'll try to describe
I got

/**
 * @ORM\Entity(repositoryClass="AppBundle\Entity\Repository\CategoryRepository")
 * @ORM\Cache(usage="NONSTRICT_READ_WRITE")
 */
class Category 
{

    /**
     * @var integer
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue
     */
    protected $id;
    /**
     * @var Media[]
     * @ORM\Cache(usage="NONSTRICT_READ_WRITE")
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\Media", cascade={"remove"}, mappedBy="category")
     * @ORM\OrderBy({"sortOrder" = "ASC"})
     */
    private $medias;
}

And accordingly I have

/**
 * @ORM\Entity(repositoryClass="AppBundle\Entity\Repository\MediaRepository")
 * @ORM\Cache(usage="NONSTRICT_READ_WRITE")
 */
class Media
{
    /**
     * @var integer
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue
     */
    private $id;
    /**
     * @var Category
     * @ORM\Cache(usage="NONSTRICT_READ_WRITE")
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Category", inversedBy="medias")
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id", nullable=false, onDelete="CASCADE")
     */
    private $category;
}

In my controller, I'm trying to get Category with Medias , sorted by sortOrder
using this query

public function findByTag(bool $enabled = true)
    {
        $qb = $this->createQueryBuilder("c", "c.id");
        $qb
            ->setCacheable(true)
            ->setCacheRegion(CategoryCache::CACHE_QUERY_CATEGORY)
            ->setLifetime(3600);

        $qb
            ->leftJoin("c.medias", "m")->addSelect("m")
            ->where($qb->expr()->eq("c.enabled", ":enabled"))
            ->andWhere($qb->expr()->eq("c.tag", ":tag"))
            ->setParameter("enabled", $enabled)
            ->setParameter("tag", $tag)
            ->orderBy("c.sortOrder");

        return $qb->getQuery()->getResult();
    }

Regions
Media is stored in media_region,
Category is stored in category_region,
QueryBuilder Query is stored in query_cache_region

And now the problem.
In case if I only change order of Media entity, I want that my order of joined entities in query also changed, but this is not working, because, I think, category_region is not changed.

\\ Doctrine\ORM\Cache\DefaultQueryCache;

/** @var $key QueryCacheKey */ 
$entry = $this->region->get($key); // CacheProvider returns cached sortOrder 
....
// $key with region set by root entity Category, so it is passed
 if ( ! $this->validator->isValid($key, $entry)) {
            $this->region->evict($key);
            return null;
}
....
foreach ($entry['associations'] as $name => $assoc) {
    ....
    foreach ($assoc['list'] as $assocIndex => $assocId) { // cached sortOrder :(
        if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assoc['targetEntity'], $assocId))) === null) {
            ....
            return null; //not called,  and everything is ok
        }
    }
    ....
}

Setting custom region for query_builder is just a solution to not clear all cache for all builders.
Also, clearing association cache for CategoryEntity (my first thought, and I hope best solution) did not help, because there is no checks for that in hydration block in DefaultQueryCache code.

@lcobucci
Copy link
Member

@wtorsi sorry by the very huge delay!

We fixed several L2C bugs so would you mind checking this again using the latest version of master or even with the changes of #6244 (if it's not merged yet)?

@lcobucci lcobucci self-assigned this Jan 19, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants