This repository has been archived by the owner on Aug 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 133
MembershipManager still returns a user's memberships after the user is deleted #480
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
fc9813b
Test that the correct group memberships are returned after a user is …
pfrenssen eddd176
Use a static cache backend that supports cache tag invalidation.
pfrenssen 9cb9972
The MembershipManager cache is now cleared automatically when its cac…
pfrenssen d6baff9
Remove MembershipManager::reset().
pfrenssen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,9 @@ | |
namespace Drupal\og; | ||
|
||
use Drupal\Component\Utility\NestedArray; | ||
use Drupal\Core\Cache\Cache; | ||
use Drupal\Core\Cache\CacheBackendInterface; | ||
use Drupal\Core\Cache\CacheTagsInvalidatorInterface; | ||
use Drupal\Core\Entity\EntityInterface; | ||
use Drupal\Core\Entity\EntityTypeManagerInterface; | ||
use Drupal\Core\Session\AccountInterface; | ||
|
@@ -11,16 +14,16 @@ | |
use Drupal\og\Entity\OgMembership; | ||
|
||
/** | ||
* Membership manager. | ||
* Service for managing memberships and group content. | ||
*/ | ||
class MembershipManager implements MembershipManagerInterface { | ||
|
||
/** | ||
* Static cache of the memberships and group association. | ||
* The static cache backend. | ||
* | ||
* @var array | ||
* @var \Drupal\Core\Cache\CacheBackendInterface | ||
*/ | ||
protected $cache; | ||
protected $staticCache; | ||
|
||
/** | ||
* The entity type manager. | ||
|
@@ -43,10 +46,14 @@ class MembershipManager implements MembershipManagerInterface { | |
* The entity type manager. | ||
* @param \Drupal\og\OgGroupAudienceHelperInterface $group_audience_helper | ||
* The OG group audience helper. | ||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache | ||
* The static cache backend. | ||
*/ | ||
public function __construct(EntityTypeManagerInterface $entity_type_manager, OgGroupAudienceHelperInterface $group_audience_helper) { | ||
public function __construct(EntityTypeManagerInterface $entity_type_manager, OgGroupAudienceHelperInterface $group_audience_helper, CacheBackendInterface $cache) { | ||
assert($cache instanceof CacheTagsInvalidatorInterface, 'The cache backend must support cache tag invalidation.'); | ||
$this->entityTypeManager = $entity_type_manager; | ||
$this->groupAudienceHelper = $group_audience_helper; | ||
$this->staticCache = $cache; | ||
} | ||
|
||
/** | ||
|
@@ -85,26 +92,26 @@ public function getMemberships(AccountInterface $user, array $states = [OgMember | |
// states. | ||
$states = $this->prepareConditionArray($states, OgMembership::ALL_STATES); | ||
|
||
$identifier = [ | ||
$cid = [ | ||
__METHOD__, | ||
'user', | ||
$user->id(), | ||
implode('|', $states), | ||
]; | ||
$identifier = implode(':', $identifier); | ||
$cid = implode(':', $cid); | ||
|
||
// Use cached result if it exists. | ||
if (!isset($this->cache[$identifier])) { | ||
if (!$membership_ids = $this->staticCache->get($cid)->data ?? []) { | ||
$query = $this->entityTypeManager | ||
->getStorage('og_membership') | ||
->getQuery() | ||
->condition('uid', $user->id()) | ||
->condition('state', $states, 'IN'); | ||
|
||
$this->cache[$identifier] = $query->execute(); | ||
$membership_ids = $query->execute(); | ||
$this->cacheMembershipIds($cid, $membership_ids); | ||
} | ||
|
||
return $this->loadMemberships($this->cache[$identifier]); | ||
return $this->loadMemberships($membership_ids); | ||
} | ||
|
||
/** | ||
|
@@ -140,16 +147,16 @@ public function getGroupMembershipIdsByRoleNames(EntityInterface $group, array $ | |
$role_names = $this->prepareConditionArray($role_names); | ||
$states = $this->prepareConditionArray($states, OgMembership::ALL_STATES); | ||
|
||
$identifier = [ | ||
$cid = [ | ||
__METHOD__, | ||
$group->id(), | ||
implode('|', $role_names), | ||
implode('|', $states), | ||
]; | ||
$identifier = implode(':', $identifier); | ||
$cid = implode(':', $cid); | ||
|
||
// Only query the database if no cached result exists. | ||
if (!isset($this->cache[$identifier])) { | ||
if (!$membership_ids = $this->staticCache->get($cid)->data ?? []) { | ||
$entity_type_id = $group->getEntityTypeId(); | ||
|
||
$query = $this->entityTypeManager | ||
|
@@ -168,10 +175,11 @@ public function getGroupMembershipIdsByRoleNames(EntityInterface $group, array $ | |
$query->condition('roles', $role_ids, 'IN'); | ||
} | ||
|
||
$this->cache[$identifier] = $query->execute(); | ||
$membership_ids = $query->execute(); | ||
$this->cacheMembershipIds($cid, $membership_ids); | ||
} | ||
|
||
return $this->cache[$identifier]; | ||
return $membership_ids; | ||
} | ||
|
||
/** | ||
|
@@ -205,29 +213,31 @@ public function getGroupIds(EntityInterface $entity, $group_type_id = NULL, $gro | |
throw new \InvalidArgumentException('\Drupal\og\MembershipManager::getGroupIds() cannot be used for user entities. Use \Drupal\og\MembershipManager::getUserGroups() instead.'); | ||
} | ||
|
||
$identifier = [ | ||
$cid = [ | ||
__METHOD__, | ||
$entity->getEntityTypeId(), | ||
$entity->id(), | ||
$group_type_id, | ||
$group_bundle, | ||
]; | ||
|
||
$identifier = implode(':', $identifier); | ||
$cid = implode(':', $cid); | ||
|
||
if (isset($this->cache[$identifier])) { | ||
if ($group_ids = $this->staticCache->get($cid)->data ?? []) { | ||
// Return cached values. | ||
return $this->cache[$identifier]; | ||
return $group_ids; | ||
} | ||
|
||
$group_ids = []; | ||
$tags = $entity->getCacheTagsToInvalidate(); | ||
|
||
$fields = $this->groupAudienceHelper->getAllGroupAudienceFields($entity->getEntityTypeId(), $entity->bundle(), $group_type_id, $group_bundle); | ||
foreach ($fields as $field) { | ||
$target_type = $field->getFieldStorageDefinition()->getSetting('target_type'); | ||
$target_type_id = $field->getFieldStorageDefinition()->getSetting('target_type'); | ||
$target_type_definition = $this->entityTypeManager->getDefinition($target_type_id); | ||
|
||
// Optionally filter by group type. | ||
if (!empty($group_type_id) && $group_type_id !== $target_type) { | ||
if (!empty($group_type_id) && $group_type_id !== $target_type_id) { | ||
continue; | ||
} | ||
|
||
|
@@ -249,9 +259,9 @@ public function getGroupIds(EntityInterface $entity, $group_type_id = NULL, $gro | |
// Query the database to get the actual list of groups. The target IDs may | ||
// contain groups that no longer exist. Entity reference doesn't clean up | ||
// orphaned target IDs. | ||
$entity_type = $this->entityTypeManager->getDefinition($target_type); | ||
$entity_type = $this->entityTypeManager->getDefinition($target_type_id); | ||
$query = $this->entityTypeManager | ||
->getStorage($target_type) | ||
->getStorage($target_type_id) | ||
->getQuery() | ||
// Disable entity access check so fetching the groups related to group | ||
// content are not affected by the current user. Furthermore, when | ||
|
@@ -266,10 +276,14 @@ public function getGroupIds(EntityInterface $entity, $group_type_id = NULL, $gro | |
$query->condition($entity_type->getKey('bundle'), $group_bundle); | ||
} | ||
|
||
$group_ids = NestedArray::mergeDeep($group_ids, [$target_type => $query->execute()]); | ||
// Add the list cache tags for the entity type, so that the cache gets | ||
// invalidated if one of the group entities is deleted. | ||
$tags = Cache::mergeTags($target_type_definition->getListCacheTags(), $tags); | ||
|
||
$group_ids = NestedArray::mergeDeep($group_ids, [$target_type_id => $query->execute()]); | ||
} | ||
|
||
$this->cache[$identifier] = $group_ids; | ||
$this->staticCache->set($cid, $group_ids, Cache::PERMANENT, $tags); | ||
|
||
return $group_ids; | ||
} | ||
|
@@ -366,13 +380,6 @@ public function isMemberBlocked(EntityInterface $group, AccountInterface $user) | |
return $this->isMember($group, $user, [OgMembershipInterface::STATE_BLOCKED]); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function reset() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO, This should have been deprecated instead of fully removed. I know we're not offering yet BC but still, given the fact that so many sites are already using OG... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right, we could have deprecated this. |
||
$this->cache = []; | ||
} | ||
|
||
/** | ||
* Prepares a conditional array for use in a cache identifier and query. | ||
* | ||
|
@@ -399,6 +406,22 @@ protected function prepareConditionArray(array $value, array $default = NULL) { | |
return array_unique($value); | ||
} | ||
|
||
/** | ||
* Stores the given list of membership IDs in the static cache backend. | ||
* | ||
* @param string $cid | ||
* The cache ID. | ||
* @param array $membership_ids | ||
* The list of membership IDs to store in the static cache. | ||
*/ | ||
protected function cacheMembershipIds($cid, array $membership_ids) { | ||
pfrenssen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
$tags = Cache::buildTags('og_membership', $membership_ids); | ||
// Also invalidate the list cache tags so that if a new membership is | ||
// created it will appear in this list. | ||
$tags = Cache::mergeTags(['og_membership_list'], $tags); | ||
$this->staticCache->set($cid, $membership_ids, Cache::PERMANENT, $tags); | ||
} | ||
|
||
/** | ||
* Returns the full membership entities with the given memberships IDs. | ||
* | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pfrenssen, I wonder why we're only doing memory caching here. This looks to me a good candidate to double the memory caching with persistent caching (database, Redis, etc.). All we need here is to declare a
\Drupal\Core\Cache\BackendChain
service and add a memory and a cache bin backends to it. Then replace the static cache service with this one. That would improve dramatically the performance.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've opened #485 to fix this.