#6284 fixing actual issue, which comes from an extremely tricky L2 caching issue.
We are not hydrating some of the cached association data into entities due to keys missing in the cache association definition. Since this is an extreme edge case that is just a mismatch between db and cache, a detailed explanation was provided in the fix snippet as well
This commit is contained in:
parent
c9d1f852de
commit
07b397f341
@ -92,13 +92,13 @@ class DefaultQueryCache implements QueryCache
|
||||
return null;
|
||||
}
|
||||
|
||||
$entry = $this->region->get($key);
|
||||
$cacheEntry = $this->region->get($key);
|
||||
|
||||
if ( ! $entry instanceof QueryCacheEntry) {
|
||||
if ( ! $cacheEntry instanceof QueryCacheEntry) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( ! $this->validator->isValid($key, $entry)) {
|
||||
if ( ! $this->validator->isValid($key, $cacheEntry)) {
|
||||
$this->region->evict($key);
|
||||
|
||||
return null;
|
||||
@ -117,11 +117,11 @@ class DefaultQueryCache implements QueryCache
|
||||
return new EntityCacheKey($cm->rootEntityName, $entry['identifier']);
|
||||
};
|
||||
|
||||
$cacheKeys = new CollectionCacheEntry(array_map($generateKeys, $entry->result));
|
||||
$cacheKeys = new CollectionCacheEntry(array_map($generateKeys, $cacheEntry->result));
|
||||
$entries = $region->getMultiple($cacheKeys);
|
||||
|
||||
// @TODO - move to cache hydration component
|
||||
foreach ($entry->result as $index => $entry) {
|
||||
foreach ($cacheEntry->result as $index => $entry) {
|
||||
$entityEntry = is_array($entries) && array_key_exists($index, $entries) ? $entries[$index] : null;
|
||||
|
||||
if ($entityEntry === null) {
|
||||
@ -210,6 +210,25 @@ class DefaultQueryCache implements QueryCache
|
||||
$collection->setInitialized(true);
|
||||
}
|
||||
|
||||
foreach ($data as $fieldName => $unCachedAssociationData) {
|
||||
// In some scenarios, such as EAGER+ASSOCIATION+ID+CACHE, the
|
||||
// cache key information in `$cacheEntry` will not contain details
|
||||
// for fields that are associations.
|
||||
//
|
||||
// This means that `$data` keys for some associations that may
|
||||
// actually not be cached will not be converted to actual association
|
||||
// data, yet they contain L2 cache AssociationCacheEntry objects.
|
||||
//
|
||||
// We need to unwrap those associations into proxy references,
|
||||
// since we don't have actual data for them except for identifiers.
|
||||
if ($unCachedAssociationData instanceof AssociationCacheEntry) {
|
||||
$data[$fieldName] = $this->em->getReference(
|
||||
$unCachedAssociationData->class,
|
||||
$unCachedAssociationData->identifier
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$result[$index] = $this->uow->createEntity($entityEntry->class, $data, self::$hints);
|
||||
}
|
||||
|
||||
@ -246,7 +265,6 @@ class DefaultQueryCache implements QueryCache
|
||||
$data = [];
|
||||
$entityName = reset($rsm->aliasMap);
|
||||
$rootAlias = key($rsm->aliasMap);
|
||||
$hasRelation = ( ! empty($rsm->relationMap));
|
||||
$persister = $this->uow->getEntityPersister($entityName);
|
||||
|
||||
if ( ! ($persister instanceof CachedPersister)) {
|
||||
@ -258,8 +276,6 @@ class DefaultQueryCache implements QueryCache
|
||||
foreach ($result as $index => $entity) {
|
||||
$identifier = $this->uow->getEntityIdentifier($entity);
|
||||
$entityKey = new EntityCacheKey($entityName, $identifier);
|
||||
$data[$index]['identifier'] = $identifier;
|
||||
$data[$index]['associations'] = [];
|
||||
|
||||
if (($key->cacheMode & Cache::MODE_REFRESH) || ! $region->contains($entityKey)) {
|
||||
// Cancel put result if entity put fail
|
||||
@ -268,9 +284,8 @@ class DefaultQueryCache implements QueryCache
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $hasRelation) {
|
||||
continue;
|
||||
}
|
||||
$data[$index]['identifier'] = $identifier;
|
||||
$data[$index]['associations'] = [];
|
||||
|
||||
// @TODO - move to cache hydration components
|
||||
foreach ($rsm->relationMap as $alias => $name) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user