[DDC-2295] [GH-580] Second cache level POC Created: 14/Feb/13  Updated: 16/Dec/13  Resolved: 16/Dec/13

Status: Resolved
Project: Doctrine 2 - ORM
Component/s: None
Affects Version/s: None
Fix Version/s: None
Security Level: All

Type: New Feature Priority: Major
Reporter: Benjamin Eberlei Assignee: Benjamin Eberlei
Resolution: Duplicate Votes: 0
Labels: None


 Description   

This issue is created automatically through a Github pull request on behalf of FabioBatSilva:

Url: https://github.com/doctrine/doctrine2/pull/580

Message:

Hi guys.

After a look into some implementations I end up with the following solution for the second level cache..

There is lot of work todo before merge it, but i'd like to get your thoughts before i go any further on this approach.
I hope my drafts are good enough to explain the idea :

      1. Cache strategies
  • READ_ONLY (DEFAULT) : ReadOnly cache can do reads, inserts and deletes, cannot perform updates or employ any locks.
  • NONSTRICT_READ_WRITE : Nonstrict Read Write Cache doesn’t employ any locks but can do reads, inserts , updates and deletes.
  • NONSTRICT_READ_WRITE : Read Write cache employs locks the entity before update/delete.
      1. classes / interfaces
  • *Region* :
    Defines a contract for accessing a entity/collection data cache. (Doesn’t employ any locks)
  • *ConcurrentRegion* :
    Defines contract for concurrently managed data region. (Locks the data before update/delete.)
  • *RegionAccess* :
    Defines a contract to access a cache region
  • *ConcurrentRegionAccess* :
    Defines contract for regions which hold concurrently managed data.
  • *CacheKey / EntityCacheKey / CollectionCacheKey/ QueryCacheKey*:
    Defines entity / collection key to be stored in the cache region.
  • *EntityEntryStructure / CollectionEntryStructure*
    Build cache entries and rebuild entities/colection from cache
  • *AccessProvider*
    Build RegionAccess based on entity / collection cache configuration

Collection Caching

The most common use case is to cache entities. But we can also cache relationships. 
A “collection cache” caches the primary keys of entities that are members of a collection (OneToMany/ManyToMany). 
and each element will be cached into its region.

Only identifiers will be cached for collection. When a collection is read from the second level cache it will create proxies based on the cached identifiers, if the application needs to access an element, Doctrine will go to the cache to load the element data.

      1. OPERATIONS
        1. INSERT :

*********************************************************************************************
UnitOfWork#commit
Connection#beginTransaction
Persister#executeInserts
Connection#commit
Persister#afterTransactionComplete
-> EntityRegionAccessStrategy#afterInsert
*********************************************************************************************
METHOD | READ-ONLY | NONSTRICT-READ-WRITE | READ-WRITE |
---------------------------------------------------------------------------------------------
afterInsert | add item to the cache | add item to the cache | add item to the cache |
---------------------------------------------------------------------------------------------

        1. UPDATE :

*********************************************************************************************
UnitOfWork#commit
Connection#beginTransaction
Persister#update
-> TransactionalRegionAccess#lockItem
-> execute
Connection#commit
Persister#afterTransactionComplete
-> RegionAccess#afterUpdate
-> TransactionalRegionAccess#unlockItem
*********************************************************************************************
METHOD | READ-ONLY | NONSTRICT-READ-WRITE | READ-WRITE |
---------------------------------------------------------------------------------------------
lockItem | | | lock item |
---------------------------------------------------------------------------------------------
afterUpdate | throws exception | update item cache | update item cache |
---------------------------------------------------------------------------------------------
unlockItem | | | unlock item |
---------------------------------------------------------------------------------------------

        1. DELETE :

*********************************************************************************************
UnitOfWork#commit
Connection#beginTransaction
Persister#delete
-> TransactionalRegionAccess#lockItem
-> execute
Connection#commit
Persister#afterTransactionComplete
-> RegionAccess#evict
*********************************************************************************************
METHOD | READ-ONLY | NONSTRICT-READ-WRITE | READ-WRITE |
---------------------------------------------------------------------------------------------
lockItem | | | lock item |
---------------------------------------------------------------------------------------------
evict | remove item cache | remove item cache | remove item cache |
---------------------------------------------------------------------------------------------

        1. USAGE :
```php
<?php

/**
 * @Entity
 * @Cache("NONSTRICT_READ_WRITE")
 */
class State
{
    /**
     * @Id
     * @GeneratedValue
     * @Column(type="integer")
     */
    protected $id;
    /**
     * @Column
     */
    protected $name;
    /**
     * @Cache()
     * @ManyToOne(targetEntity="Country")
     * @JoinColumn(name="country_id", referencedColumnName="id")
     */
    protected $country;
    /**
     * @Cache()
     * @OneToMany(targetEntity="City", mappedBy="state")
     */
    protected $cities;
}
```

```php
<?php

$em->persist(new State($name, $country));
$em->flush();                                // Put into cache

$em->clear();                                // Clear entity manager

$state   = $em->find('Entity\State', 1);     // Retreive item from cache
$country = $state->getCountry();             // Retreive item from cache
$cities  = $state->getCities();              // Load from database and put into cache

$state->setName("New Name");
$em->persist($state);
$em->flush();                                // Update item cache

$em->clear();                                // Clear entity manager

$em->find('Entity\State', 1)->getCities();   // Retreive from cache


$em->getCache()->containsEntity('Entity\State', $state->getId())  // Check if the cache exists
$em->getCache()->evictEntity('Entity\State', $state->getId());    // Remove an entity from cache
$em->getCache()->evictEntityRegion('Entity\State');               // Remove all entities from cache

$em->getCache()->containsCollection('Entity\State', 'cities', $state->getId());   // Check if the cache exists        
$em->getCache()->evictCollection('Entity\State', 'cities', $state->getId());      // Remove an entity collection from cache
$em->getCache()->evictCollectionRegion('Entity\State', 'cities');                 // Remove all collections from cache

```
        1. TODO :
  • handle many to many collection
  • handle inheritance
  • remove/add colection items on update
  • improve region tests
  • improve access strategy tests
  • implement xml / yml / php drivers
  • implement transaction region
  • implement transaction access strategy
  • .... ????


 Comments   
Comment by Doctrine Bot [ 01/Oct/13 ]

A related Github Pull-Request [GH-580] was closed:
https://github.com/doctrine/doctrine2/pull/580

Comment by Sergii Shymko [ 21/Nov/13 ]

Is the proposed solution going to allow to cache scalar properties of entities of specific type?

The behavior I'm looking for is the following:

  • Certain entity fields are explicitly marked as cached via annotation or something
  • Regardless of how a partially loaded entity is acquired (EM, repository, DQL), those fields are populated with the cached data
  • Getters for those fields simply return assigned values, not causing the full entity load, similarly to identifier fields
Comment by Marco Pivetta [ 22/Nov/13 ]

Sergii Shymko no, that is not covered. That would require a major overhaul in code generation tools and it would introduce major rewrites of the internal components. It's not covered here and is not really related with implementing the L2 Cache.

Generated at Sat Oct 25 20:48:27 UTC 2014 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.