Deleting the local roles is what takes up the memory: it goes through the whole site, waking up all objects, which can be a lot if you have a big site.
For one client where we ran into this, we solved it with the following patch.
Note that you may need to tweak the depth option to fit your site.
The numbers mentioned in the docstring are for one specific site.
from Acquisition import aq_base
from Products.CMFCore.utils import _checkPermission
from Products.CMFCore.MembershipTool import MembershipTool
def deleteLocalRoles(self, obj, member_ids, reindex=1, recursive=0,
REQUEST=None, depth=3):
"""Delete local roles of specified members.
This takes far too much memory. See if we can reduce this.
We have tried convincing Zope to release memory by using
savepoints (transaction.savepoint(optimistic=True)). We tried
using the catalog to search for only folderish items, and do
explicit garbage collection. Nothing helped.
Now we add a depth on which we search. This is a fluid depth.
If the object does not need deletion of local roles and there
are no interesting local roles at all, we decrease the depth,
thus eliminating uninteresting folders where the member likely
has no local roles anywhere.
Yes, this may fail to delete some local roles. But at least it
usually finishes within a few seconds instead of about a minute.
And it does not consume over 1.5 GB of memory. So be happy.
Note: with a depth of 4 you would already crawl about 90 percent
of the site, so that would hardly help.
"""
delete = False
if _checkPermission(ChangeLocalRoles, obj):
has_local_roles = False
for user, roles in obj.get_local_roles():
if user in member_ids:
delete = True
if len(roles) == 0:
# I guess this cannot happen, but let's be safe.
continue
elif len(roles) > 1:
has_local_roles = True
break
elif roles[0] == 'Owner':
# Only one uninteresting role.
continue
else:
has_local_roles = True
break
if delete:
# At least one to-be-deleted role has been found.
obj.manage_delLocalRoles(userids=member_ids)
elif not has_local_roles:
# Nothing deleted at this level, and no interesting local
# roles for other users. Decrease search depth.
depth -= 1
if depth <= 0:
# Ignore the rest of this content tree, if any.
return
if recursive and hasattr(aq_base(obj), 'contentValues'):
for subobj in obj.contentValues():
self.deleteLocalRoles(subobj, member_ids, 0, 1, depth=depth)
if reindex and hasattr(aq_base(obj), 'reindexObjectSecurity'):
# reindexObjectSecurity is always recursive
obj.reindexObjectSecurity()
def patch_membershiptool():
MembershipTool._orig_deleteLocalRoles = MembershipTool.deleteLocalRoles
MembershipTool.deleteLocalRoles = deleteLocalRoles
def unpatch_membershiptool():
MembershipTool.deleteLocalRoles = MembershipTool._orig_deleteLocalRoles
thanks, I ended with this and it seems to be working now:
def remove_subscriber(self, email):
"""Remove subscriber. We can not use `api.user.delete` as, by
default, it tries to remove member areas and local roles; the
later consumes a lot of memory and is not necessary for us.
"""
membership_tool = api.portal.get_tool('portal_membership')
with api.env.adopt_roles(['Manager']):
membership_tool.deleteMembers(
[email], delete_memberareas=False, delete_localroles=False)
Has anyone recently dealt with the situation of deleting a user from a content-heavy site? Deleting a user on sites like that takes a long time because of the removal of local roles. Any ideas? I saw this:
If local roles are not a thing in your site, or you are pretty sure that user ids will not clash in the future, you can skip the local role checking when deleting the users.
the allowed roles and users Index reads the value from the localRole. Plone has no way to know the user has been deleted, think to external authentication for example. Users are checked at the time they try to do something. Showing the state depends on the security info in the object. The only way is to fix the permission on the object.
The only way I see it to never use users but groups. So, if you've UserA, create the group UserA with the UserA and use the group for permission instead of the user. So deletion is just about removing the user from the group and deleting the user.
Generally speaking, use groups and never assign role or permissions to the single user.