[DDC-217] Result cache should cache the SQL result and not the final objects Created: 19/Dec/09  Updated: 19/Dec/11  Resolved: 30/Oct/11

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

Type: Improvement Priority: Minor
Reporter: Roman S. Borschel Assignee: Benjamin Eberlei
Resolution: Fixed Votes: 5
Labels: None

Issue Links:
is duplicated by DDC-1279 Shouldn't Cached Results Trigger Life... Resolved
is referenced by DDC-446 Cached resultset do not add Entities ... Resolved


When fetching objects and using a result cache, it would probably be better to cache the SQL result array instead of the hydrated objects. That way, when grabbing from the cache managed entities can be returned. Also, caching is more efficient since an SQL result array does not need to be serialized/unserialized when storing in apc or memcached.

Comment by Benjamin Eberlei [ 31/Jan/10 ]

It also avoids unnecessary "proxy not initialized, can't serialize" exceptions.

Comment by Jonathan H. Wage [ 18/Mar/10 ]

If we make this change, the caching won't be "as" affective right since it will still have to go through the hydration process?

Comment by Benjamin Eberlei [ 18/Mar/10 ]

Correct, but its almost impossible to result cache entities because they are not allowed to have a single proxy entity at any depth of the object graph.

Maybe we should introduce another notion of SqlResultCache? The ResultCache is good and effective for all Array and Scalar hydration modes, however the SQL Result caching is more useful for Object Hydration.

Comment by Roman S. Borschel [ 18/Mar/10 ]

Exactly. Yes it means hydration is always run but you really cant speak of "effective" when caching large object result sets because serialization & especially unserialization which then happens on every request will be pretty slow.

As Benjamin said, result caching is perfect for anything but objects so this ticket really only affects queries that use object hydration. Storing the raw sql result for these has the advantage that the objects retrieved from the cache are not detached and thus need not be merged and also you dont have issues with uninitialized proxies preventing serialization.

Comment by Jonathan H. Wage [ 20/Mar/10 ]

Is this also the cause of this issue? http://www.doctrine-project.org/jira/browse/DDC-446

Since we're unserializing some objects, does Doctrine know about them?

Comment by Roman S. Borschel [ 20/Mar/10 ]

Yes, as already said, objects coming from the result cache are currently DETACHED. Thus the EM does not care about them. This is fine if you just want to display them but if you want to modify them and persist new changes, you need to merge() the ones you want to become MANAGED again into the EM.

Comment by Roman S. Borschel [ 20/Mar/10 ]

It should also be noticed that if we make this change and the cached objects are managed after grabbing from the cache this can have a negative effect on flushing performance, if you only want the cached objects for read-only/display purposes. Maybe we should still provide both options, like Benjamin said.

Comment by Roman S. Borschel [ 07/Aug/10 ]

Moved to 2.1 for now.

Comment by Daniel Cousineau [ 30/Nov/10 ]

I would like to see the ability to cache pre-hydration as I have already run into this issue.

Maybe the interface could be along similar syntaxes as turning on result caches

      ->setResultCachePreHydration(true); //Result cache should work pre-hydration, false caches 

However from my examinations the hydrators will have to be rewritten to accept an array instead of a pdo statement.

That would be my vote. If you don't have the resources to work on the ticket I can tackle it.

Comment by Benjamin Eberlei [ 01/Dec/10 ]

rewriting to use an array is not an option. this would require to call ->fetchAll() which is not wanted for the Iterate Result feature.

There has to be a better way to redefine the method signatures to allow both use-cases.

Comment by Benjamin Eberlei [ 01/Dec/10 ]

Ah btw, any help is appreciated. Just Fork doctrine2 over at Github and develop this feature in a new branch (git checkout -b DDC-217) and link it here or issue a pull request on Github.

Comment by Daniel Cousineau [ 01/Dec/10 ]

Forked here: https://github.com/dcousineau/doctrine2 though probably should pull it out into a local branch.

I went ahead and went down that route of altering hydrators to accept an array as well as a statement. This way uncached queries will work with the statement as it used to, but can also hydrate from an array.

As for the iterable hydrator you have a point but in this case you really can't cache it period, be it pre or post hydration (since to cache we have to serialize ALL the information).

What I have up is working(ish with the caveat that I backported the changes to an earlier tagged release for our application and tested using that), take a look and see if we like or don't like how I did it.

You'll notice I did split up _doExecute() for the query objects, because the DQL query object does so much analysis in the _doExecute() method which produces and attaches information (namely the result set mapping) that is depended on by the hydration process, I split it up into a "doPreExecute" function so that it can be called separately without actually executing the query.

Comment by Benjamin Eberlei [ 02/Dec/10 ]

Yes i know that the serialize thing won't work with the Iterator. My point was that the Iterator USES the Hydrator code and it should still support that use-case.

See the IterableResult class and make sure that its still working after your refactoring.

I don't like the instanceof check then iterate vs the forach over the array. I would rather find a solutio nthat works for both, but i cant find one now that doesnt involve findAll() (which is bad).

Comment by Daniel Cousineau [ 02/Dec/10 ]

I'll double check against the IterableResult and make sure it's compatible.

Beyond that, I don't know what I could other than the instance of check. I could extend the statement object and create a statement that takes a flat array so all the code remains the same (i'm passing a statement).

Comment by Benjamin Eberlei [ 24/Dec/10 ]

Assigning Guilherme

Comment by Przemek Sobstel [ 10/May/11 ]

I've made my own implementation for this:

There are no changes in specific hydrators and there is no fetchAll(). I've added 2 wrappers: one for cached result array, and one for uncached pdo statement. The former (CachedResultStatement) is actually ArrayIterator which just implements used methods (fetch & closeCursor => also specified in ResultStatementInterface). The latter is decorator for stmt, which just collects results on each call of fetch() (results are later available by calling getResult), and apart from this all calls are delegated to actual stmt object.

I've used (copied) following changes from Daniel https://github.com/dcousineau/doctrine2/commit/c23b5a902da79ffc472c0769b9258775baf3189e

Btw, what is your reasoning for not using fetchAll() anywhere?

Comment by Reio Piller [ 19/Jun/11 ]

maybe a dumb question but is it normal that result cache fetched entities associations are set to blank objects?

Comment by Bogdan Albei [ 22/Jul/11 ]

I've sent a pull request for the fix(https://github.com/bogdanalbei/doctrine2/commit/00d1aa2192b86e51872fbc833b2436354f6fe162) . The changes were tested under live conditions sice v2.0.

Comment by Benjamin Eberlei [ 17/Oct/11 ]

Workaround for this issue: https://gist.github.com/57e6b4deea566baac053

Also: This will be fixed in 2.2.

Comment by Benjamin Eberlei [ 22/Oct/11 ]

First step of this hit DBAL today https://github.com/doctrine/dbal/pull/69

Comment by Benjamin Eberlei [ 23/Oct/11 ]

DBAL code was heavily refactored and merged, the ORM PR is now open for discussion:


Comment by Benjamin Eberlei [ 30/Oct/11 ]

Merged into master

Comment by Benjamin Eberlei [ 19/Dec/11 ]

Related Pull Request was closed: https://github.com/doctrine/doctrine2/pull/87

Generated at Wed Oct 07 08:28:47 EDT 2015 using JIRA 6.4.10#64025-sha1:5b8b74079161cd76a20ab66dda52747ee6701bd6.