[Freeipa-devel] [PATCH] 1 Do lazy initializiation ipalib

Alexander Bokovoy abokovoy at redhat.com
Wed Nov 2 10:42:06 UTC 2011


On Mon, 31 Oct 2011, Jan Cholasta wrote:
> Dne 31.10.2011 13:19, Alexander Bokovoy napsal(a):
> >On Mon, 31 Oct 2011, Jan Cholasta wrote:
> >>Added finalization for __call__ and the check for CLI. Patch attached.
> >ACK from my side but see below.
> >
> >>+    def __getattribute__(self, name):
> >>+        if not name.startswith('_Plugin__') and not name.startswith('_ReadOnly__') and name != 'finalize_late':
> >>+            self.finalize_late()
> >>+        return object.__getattribute__(self, name)
> >Could you get faster than three string comparisons? As
> >__getattribute__ is fairly often called it would make sense to keep
> >these operations to absolute minimum.
> >
> 
> Is there any noticable slowdown?
Yes. Now I have different patch to solve this issue that avoids using 
__getattribute__. Instead, it sets a trap on attributes that are 
changed by finalization process and when they are accessed first time, 
the trap forces instance to finalize. As result, the attributes get 
their proper values and traps are removed, no performance costs 
anymore.

For Commands one additional check is done on __call__() method to 
verify that we are indeed finalized before execution proceeds. It is a 
safety net here.

Performance is not bad:

1. Before the patch
    [root at vm-114 ipalib]# time ipa >/dev/null
     
    real 0m1.101s
    user 0m0.930s
    sys 0m0.151s
    [root at vm-114 ipalib]# time ipa user-find>/dev/null
     
    real 0m3.132s
    user 0m0.983s
    sys 0m0.135s

2. With patch
    [root at vm-114 ipalib]# patch -p2 <~/speedup.patch
    patching file frontend.py
    patching file plugable.py
    [root at vm-114 ipalib]# time ipa >/dev/null
     
    real 0m0.563s
    user 0m0.438s
    sys 0m0.098s
    [root at vm-114 ipalib]# time ipa >/dev/null
     
    real 0m0.521s
    user 0m0.412s
    sys 0m0.100s
    [root at vm-114 ipalib]# time ipa user-find>/dev/null
     
    real 0m1.069s
    user 0m0.445s
    sys 0m0.111s
    [root at vm-114 ipalib]# time ipa user-find>/dev/null
     
    real 0m0.840s
    user 0m0.425s
    sys 0m0.126s
    [root at vm-114 ipalib]# time ipa user-find>/dev/null
     
    real 0m0.816s
    user 0m0.432s
    sys 0m0.119s

Patch is attached.
-- 
/ Alexander Bokovoy
-------------- next part --------------
>From d28b13f9de7d41b25c51aa7c26ca2b09f8671e6b Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Wed, 2 Nov 2011 12:24:20 +0200
Subject: [PATCH] Perform late initialization of FreeIPA plugins

https://fedorahosted.org/freeipa/ticket/1336

When plugins are loaded, instances of the provided objects and commands
are registered in the API instance. The patch changes finalization process
to apply on first access to the Command instance that would cause either
access to properties (args, options, params) or execution of the command
itself.

The patch gives 2x boost for client-side commands like help and 3x boost
for commands that go to the server side. All performance numbers are
approximate and may vary a lot.
---
 ipalib/frontend.py |   20 ++++++++++++++++++++
 ipalib/plugable.py |    6 ++----
 2 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/ipalib/frontend.py b/ipalib/frontend.py
index 61e7f493f8a8e30a1a189d06cd6a69893319deaf..a3fed2bbf0fb4631e238fad9892fb8129dbd6ca1 100644
--- a/ipalib/frontend.py
+++ b/ipalib/frontend.py
@@ -404,6 +404,22 @@ class Command(HasParam):
     msg_summary = None
     msg_truncated = _('Results are truncated, try a more specific search')
 
+    def __init__(self):
+        class Finalizer(object):
+            def __init__(self, master, attr):
+                self.master = master
+                self.attr = attr
+
+            def __call__(self):
+                self.master.finalize()
+                # At this point master.attr points to proper object
+                return self.master.__dict__[self.attr]()
+
+        self.args = Finalizer(self, 'args')
+        self.options = Finalizer(self, 'options')
+        self.params = Finalizer(self, 'params')
+        super(Command, self).__init__()
+
     def __call__(self, *args, **options):
         """
         Perform validation and then execute the command.
@@ -411,6 +427,10 @@ class Command(HasParam):
         If not in a server context, the call will be forwarded over
         XML-RPC and the executed an the nearest IPA server.
         """
+        # Plugin instance must be finalized before we get to execution
+        if not self.__dict__['_Plugin__finalized']:
+            self.finalize()
+
         params = self.args_options_2_params(*args, **options)
         self.debug(
             'raw: %s(%s)', self.name, ', '.join(self._repr_iter(**params))
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
index b0e415656e0428eb164c35a2862fcfbf50883381..ee29929c0a6de776724659e03194973937111868 100644
--- a/ipalib/plugable.py
+++ b/ipalib/plugable.py
@@ -207,6 +207,7 @@ class Plugin(ReadOnly):
                     self.label
                 )
             )
+        self.__finalized = False
 
     def __get_api(self):
         """
@@ -220,6 +221,7 @@ class Plugin(ReadOnly):
     def finalize(self):
         """
         """
+        self.__finalized = True
         if not is_production_mode(self):
             lock(self)
 
@@ -637,10 +639,6 @@ class API(DictProxy):
             if not production_mode:
                 assert p.instance.api is self
 
-        for p in plugins.itervalues():
-            p.instance.finalize()
-            if not production_mode:
-                assert islocked(p.instance) is True
         object.__setattr__(self, '_API__finalized', True)
         tuple(PluginInfo(p) for p in plugins.itervalues())
         object.__setattr__(self, 'plugins',
-- 
1.7.7



More information about the Freeipa-devel mailing list