Commit ea56fe72 authored by Vincent Pelletier's avatar Vincent Pelletier

ERP5Type: Patch AccessControl.owner.Owned.getWrappedOwner .

This method is called on every __getattr__ (guarded_getattr, actually) of
every restricted python scripts. Which means each "." in the code triggers:
- traversal to relevant acl_user folder
- a user lookup
just to get the same value throughout the execution of a script (as its
owner does not change during execution).
Also, in practice we have extremely few possible owners: very few users are
allowed to edit code in an ERP5 instance, and if such instance is managed
using the upgrader alarms, they will even further reduce this owner set to
System Processes only.
On a real-world web page rendering, this reduces the total number of
traversal calls from 1500 to 1100, getting rid of the two hottest spots:
/acl_users and /$site_id/acl_users .
parent 8d2764f2
......@@ -21,6 +21,7 @@
##############################################################################
# Load all monkey patches
from Products.ERP5Type.patches import AccessControl_patch
from Products.ERP5Type.patches import Restricted
from Products.ERP5Type.patches import m2crypto
from Products.ERP5Type.patches import ObjectManager
......
##############################################################################
#
# Copyright (c) 2002 Zope Foundation and Contributors.
# Copyright (c) 2018 Nexedi SARL and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
from __future__ import absolute_import
import AccessControl.owner
from AccessControl import SpecialUsers as SU
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
# Patch description:
# Original method is called very often: multiple times per restricted python
# script/expression: once per __getattr__ (restricted getattr, actually) call.
# Each call pulls self's owner information, traverses to relevant user database
# and then use it to retrieve user object. These last two operations are
# expensive, and should produce the same result within a given transaction.
# So cache the first result for each owner tuple in a transactional cache.
UnownableOwner = AccessControl.owner.UnownableOwner
def getWrappedOwner(self):
"""Get the owner, modestly wrapped in the user folder.
o If the object is not owned, return None.
o If the owner's user database doesn't exist, return Nobody.
o If the owner ID does not exist in the user database, return Nobody.
"""
owner = self.getOwnerTuple()
if owner is None or owner is UnownableOwner:
return None
udb_path, oid = owner
cache_key = ('getWrappedOwner', ) + (
(tuple(udb_path), oid)
if isinstance(udb_path, list) else
owner
)
try:
return getTransactionalVariable()[cache_key]
except KeyError:
pass
root = self.getPhysicalRoot()
udb = root.unrestrictedTraverse(udb_path, None)
if udb is None:
result = SU.nobody
else:
user = udb.getUserById(oid, None)
if user is None:
result = SU.nobody
else:
result = user.__of__(udb)
getTransactionalVariable()[cache_key] = result
return result
AccessControl.owner.Owned.getWrappedOwner = getWrappedOwner
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment