]> git.ipfire.org Git - ipfire.org.git/commitdiff
accounts: Use heavy caching to take off load from LDAP server
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 7 May 2019 10:49:25 +0000 (11:49 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 7 May 2019 10:49:25 +0000 (11:49 +0100)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/backend/accounts.py

index dfdebf98e6426b7f6526bf964a3e7e7ae0c76502..784ddabc2ea98b389637ce2ed0fb745907aa9a1a 100644 (file)
@@ -49,13 +49,13 @@ class Accounts(Object):
 
                return conn
 
-       def _query(self, query, attrlist=None, limit=0):
+       def _query(self, query, attrlist=None, limit=0, search_base=None, scope=None):
                logging.debug("Performing LDAP query: %s" % query)
 
                t = time.time()
 
-               results = self.ldap.search_ext_s(self.search_base, ldap.SCOPE_SUBTREE,
-                       query, attrlist=attrlist, sizelimit=limit)
+               results = self.ldap.search_ext_s(search_base or self.search_base,
+                       scope or ldap.SCOPE_SUBTREE, query, attrlist=attrlist, sizelimit=limit)
 
                # Log time it took to perform the query
                logging.debug("Query took %.2fms" % ((time.time() - t) * 1000.0))
@@ -63,8 +63,47 @@ class Accounts(Object):
                return results
 
        def _search(self, query, attrlist=None, limit=0):
-               for dn, attrs in self._query(query, attrlist=attrlist, limit=limit):
-                       yield Account(self.backend, dn, attrs)
+               dns = self._search_dns(query, limit=limit)
+
+               return self._get_accounts_from_dns(dns)
+
+       def _search_dns(self, query, limit=0):
+               # Query cache
+               dns = self.memcache.get("accounts:search:%s" % query)
+               if dns is None:
+                       # Return DNs only for this search query
+                       dns = []
+                       for dn, attrs in self._query(query, attrlist=["dn"], limit=limit):
+                               dns.append(dn)
+
+                       # Cache for 10 min
+                       self.memcache.set("accounts:search:%s" % query, dns, 600)
+
+               return dns
+
+       def _get_accounts_from_dns(self, dns):
+               for dn in dns:
+                       yield self._get_account_from_dn(dn)
+
+       def _get_account_from_dn(self, dn):
+               attrs = self.memcache.get("accounts:%s:attrs" % dn)
+               if attrs is None:
+                       attrs = self._get_attrs(dn)
+                       assert attrs, dn
+
+                       # Cache all attributes for 5 min
+                       self.memcache.set("accounts:%s:attrs" % dn, attrs, 300)
+
+               return Account(self.backend, dn, attrs)
+
+       def _get_attrs(self, dn):
+               """
+                       Fetches all attributes for the given distinguished name
+               """
+               results = self._query("(objectClass=*)", search_base=dn, limit=1)
+
+               for dn, attrs in results:
+                       return attrs
 
        def search(self, query):
                # Search for exact matches
@@ -105,8 +144,6 @@ class Accounts(Object):
        def get_by_mail(self, mail):
                return self._search_one("(&(objectClass=inetOrgPerson)(mail=%s))" % mail)
 
-       find = get_by_uid
-
        def find_account(self, s):
                account = self.get_by_uid(s)
                if account:
@@ -257,6 +294,9 @@ class Account(Object):
                # Run modify operation
                self.ldap.modify_s(self.dn, modlist)
 
+               # Delete cached attributes
+               self.memcache.delete("accounts:%s:attrs")
+
        def _set(self, key, values):
                current = self._get(key)