DDC-217: Result cache should cache the SQL result and not the final objects #2861

Closed
doctrinebot opened this Issue Dec 19, 2009 · 25 comments

Projects

None yet

2 participants

@doctrinebot

Jira issue originally created by user romanb:

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.

@doctrinebot

Comment created by @beberlei:

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

@doctrinebot

Comment created by @jwage:

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

@doctrinebot

Comment created by @beberlei:

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.

@doctrinebot

Comment created by romanb:

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.

@doctrinebot

Comment created by @jwage:

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

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

@doctrinebot

Comment created by romanb:

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.

@doctrinebot

Comment created by romanb:

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.

@doctrinebot

Comment created by romanb:

Moved to 2.1 for now.

@doctrinebot

Comment created by dcousineau:

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

$query->useResultCache(true)
      ->setResultCacheLifetime(3600)
      ->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.

@doctrinebot

Comment created by @beberlei:

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.

@doctrinebot

Comment created by @beberlei:

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.

@doctrinebot

Comment created by dcousineau:

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.

@doctrinebot

Comment created by @beberlei:

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).

@doctrinebot

Comment created by dcousineau:

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).

@doctrinebot

Comment created by @beberlei:

Assigning Guilherme

@doctrinebot

Comment created by sobstel:

I've made my own implementation for this:
sobstel@f583625

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 dcousineau@c23b5a9

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

@doctrinebot

Comment created by hypno:

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

@doctrinebot

Comment created by bogdan.albei:

I've sent a pull request for the fix(bogdanalbei@00d1aa2) . The changes were tested under live conditions sice v2.0.

@doctrinebot

Comment created by @beberlei:

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

Also: This will be fixed in 2.2.

@doctrinebot

Comment created by @beberlei:

First step of this hit DBAL today doctrine/dbal#69

@doctrinebot

Comment created by @beberlei:

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

#172

@doctrinebot

Comment created by @beberlei:

Merged into master

@doctrinebot

Issue was closed with resolution "Fixed"

@doctrinebot

Comment created by @beberlei:

Related Pull Request was closed: #87

@beberlei beberlei was assigned by doctrinebot Dec 6, 2015
@doctrinebot doctrinebot added this to the 2.2 milestone Dec 6, 2015
@doctrinebot doctrinebot closed this Dec 6, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment